master-migration-6-beta#64
Conversation
This fixes codegen not detecting library
* fix: video in fullscreen freezing after swiping * fix(ios): use optional chainin
* chore: Upgrade to Nitro 0.35.0 * Replace `bigint` with `UInt64` * chore: Regenerate specs now for Nitro 0.35.0 * fix: Use Int64 as it can be -1 * Remove unnecessary patches * fix: change duration type to number * fix: avoid mutating VideoInformation fields after init * chore: regenerate specs with new duration type * fix: use Double for duration in VideoInformation * docs: react-native-nitro-modules version * chore: update react-native-nitro-modules version (rnv & drm) --------- Co-authored-by: fnwk <filip.wnek200@gmail.com>
* chore(nitro): update generated files * feat: implement disableAudioSessionManagement for IOS
* docs(seo): add meta titles, descriptions and keywords to all pages * docs(seo): configure sitemap, metadata, JSON-LD and enable llms.txt * docs(seo): improve homepage title and description * docs(seo): add robots.txt with AI crawler rules * docs(geo): add curated llms.txt for AI discoverability * docs: fix typo baners → banners * docs(geo): generate llms.txt via plugin instead of static file
* docs(seo): add meta titles, descriptions and keywords to all pages * docs(seo): configure sitemap, metadata, JSON-LD and enable llms.txt * docs(seo): improve homepage title and description * docs(seo): add robots.txt with AI crawler rules * docs(geo): add curated llms.txt for AI discoverability * docs: fix typo baners → banners * docs(geo): generate llms.txt via plugin instead of static file * docs(seo): refine meta titles, sidebar labels and descriptions
* Add shaka as an optional dependency and fix their type * Scaffold web * Implement VideoView for web * Implement html video properties using headless video * Implement event handlers for web * Map source * Fix package lock * Some cleanup * Fix typescript issues * Fix event listeners `this` * Rework everything to use videojs instead of shaka * Handle media sessions * Add optional `mimeType` string in source object * Add error handling for the web * Fix media session for web * Add audio track support * Add video track handling on web * Add quality selector * Handle text track change event * Add text track handling * Fix typescript * Fix native compilation due to double/long mismatch * fix(web): fix naming and typos Rename WebEventEmiter→WebEventEmitter, ViewViewProps→VideoViewProps. Fix field/comment typos and update all imports. * fix(web): critical runtime fixes Fix BigInt(NaN), player.src(undefined), codeMap fallback, videoWidth, quality guard, listener leaks, vid.style, window.videojs, MediaSession SSR. * fix(web): bridge onError to consumers and fix __destroy cleanup * refactor(web): extract WebTrackHandler and shared video.js types * feat(web): add supportedFeatures and __DEV__ warnings for unsupported methods * chore: split tsconfig into web & default * refactor(web): split events by platform and isolate DOM types * chore: remove audio/video track and quality APIs from both platforms * feat(web): migrate from video.js v8 to v10 (@videojs/react) * fix(web): proper store attach order and video element fallback * fix(web): fix container warning and store lifecycle * feat(example): web * refactor(web): move events to folder, fix error codes * refactor: remove dead code * fix(web): resize mode * refactor(web): add preload type * refactor(web): extract VideoStore type, simplify player with media getter * chore: remove .editorconfig and shell.nix * style: normalize quotes to single in non-web files * fix(web): use WeakRef for store to prevent DESTROYED errors on re-render * fix(web): fullscreen targets container, guard double enable, remove unused mimeType * feat(web): add experimental audio/video track APIs with WebVideoPlayer type * docs: add web support page * fix(web): types * fix(web): overlay radius * fix: lint errors, add missing VideoPlayerBase members, fix eslint for web files * docs: update web docs * docs: fix broken links in web support page * fix(web): add WebError type with proper HTML5 MediaError code mapping * refactor: extract WebMediaProxy for unified store/video access * lint: code * docs(web): update event note * fix(web): bufferAhead * refactor: events logic * feat: implement missins no-op fn on web * fix(web): types * fix(android): duration in seconds * fix(web): notification controls race condition * docs(web-support): update seo --------- Co-authored-by: Kamil Moskała <moskalakamil07@gmail.com> Co-authored-by: Kamil Moskała <91079590+moskalakamil@users.noreply.github.com>
| if let videoStream = manifestInfo.streams.first { | ||
| width = Double(videoStream.width ?? Int(Double.nan)) | ||
| height = Double(videoStream.height ?? Int(Double.nan)) | ||
| bitrate = Double(videoStream.bandwidth ?? Int(Double.nan)) |
There was a problem hiding this comment.
Bug: The code uses Int(Double.nan) as a fallback for optional stream info values, which causes a fatal runtime crash if the values are nil.
Severity: CRITICAL
Suggested Fix
The nil-coalescing should be performed on the optional Int before converting to a Double. Change the expression from Double(videoStream.width ?? Int(Double.nan)) to Double(videoStream.width) ?? Double.nan. This will correctly assign Double.nan as the default value when the property is nil, avoiding the fatal conversion.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location:
packages/react-native-video/ios/core/Extensions/AVURLAsset+getAssetInformation.swift#L44-L47
Potential issue: When parsing HLS stream information, if the `width`, `height`, or
`bandwidth` values are missing from the manifest, they will be `nil`. The code attempts
to provide a default value using `?? Int(Double.nan)`. However, converting `Double.nan`
to an `Int` is a fatal error in Swift and will crash the application. This is a common
scenario for HLS streams that lack complete metadata, such as audio-only streams.
| private wrapPromise<T>(promise: Promise<T>) { | ||
| return new Promise<T>((resolve, reject) => { | ||
| promise.then(resolve).catch((error) => { | ||
| reject(this.throwError(error)); | ||
| }); | ||
| }); | ||
| } |
There was a problem hiding this comment.
Bug: In wrapPromise, if an error occurs and no onError listener is set, throwError throws synchronously, preventing the promise from rejecting and causing it to hang indefinitely.
Severity: HIGH
Suggested Fix
In the .catch block of wrapPromise, wrap the call to this.throwError in a try...catch block. If throwError throws an exception, catch it and call reject with that new exception to ensure the promise properly rejects.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location: packages/react-native-video/src/core/VideoPlayer.ts#L116-L122
Potential issue: When a promise passed to `wrapPromise` rejects, the `.catch` block
calls `this.throwError(error)`. If no `onError` listener is registered, `throwError`
throws a synchronous exception. This exception occurs before the outer promise's
`reject` function is called, preventing the promise from ever settling. As a result, any
`await` on methods using `wrapPromise` (e.g., `initialize()`, `preload()`) will hang
indefinitely upon an error, leading to a silent failure and frozen operation.
| let metadataOutput = AVPlayerItemMetadataOutput() | ||
| playerItem.add(metadataOutput) | ||
| metadataOutput.setDelegate(self, queue: .global(qos: .userInteractive)) | ||
|
|
||
| let legibleOutput = AVPlayerItemLegibleOutput() | ||
| playerItem.add(legibleOutput) | ||
| metadataOutput.setDelegate(self, queue: .global(qos: .userInteractive)) |
There was a problem hiding this comment.
Bug: Local variables shadow instance properties, preventing their assignment. A copy-paste error also leaves legibleOutput without a delegate, breaking subtitle callbacks and causing a resource leak.
Severity: CRITICAL
Suggested Fix
Assign the newly created AVPlayerItemMetadataOutput and AVPlayerItemLegibleOutput to the instance properties self.metadataOutput and self.legibleOutput instead of creating new local constants. Correct the copy-paste error on line 162 to call setDelegate on legibleOutput instead of calling it on metadataOutput a second time.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location: packages/react-native-video/ios/core/VideoPlayerObserver.swift#L156-L162
Potential issue: In `VideoPlayerObserver.swift`, local constants `metadataOutput` and
`legibleOutput` are declared, shadowing the class instance properties of the same name.
These instance properties are consequently never assigned. Additionally, a copy-paste
error on line 162 results in `metadataOutput.setDelegate(...)` being called twice, while
the delegate for `legibleOutput` is never set. This prevents subtitle callbacks from
ever firing. Furthermore, the cleanup logic in `invalidatePlayerItemObservers()` checks
the instance properties, which are always `nil`, causing the outputs to never be removed
from the `AVPlayerItem` and leading to a resource leak when switching video sources.
…-Picture transitions (#4921) * fix(ios): resume playback when returning to foreground after a background pause With `playInBackground`, VideoManager leaves the player running on background and does not set `wasAutoPaused`. If the system then pauses it (background playback not possible, e.g. automatic PiP did not engage), it stayed stuck paused on return because the foreground handlers only resumed `wasAutoPaused` players. Track whether the player was playing when backgrounded and resume it on `applicationWillEnterForeground`; an explicit `pause()` clears the intent. * fix(ios): re-activate the audio session when returning to the foreground `activateAudioSession()` early-returns while the cached `isAudioSessionActive` is true. The system can deactivate the session out-of-band (an interruption, or while backgrounded), leaving the cache stale, so it is never re-activated and a resumed player plays silently. Invalidate the cache on interruption `.began` and on foreground so the session is genuinely re-activated. * fix(ios): only auto-resume after a background system pause, not other pauses The foreground recovery must resume only when the system paused background playback — not for stops that should stay paused. Clear the resume intent on: a user pause via the lock screen / Control Center / a headset (remote command center bypasses `HybridVideoPlayer.pause()`); an interruption (`.began`); headphones unplugged (`.oldDeviceUnavailable`); and the item ending. * fix(android): reset wasAutoPaused after foreground resume onAppEnterForeground resumed auto-paused players but never cleared wasAutoPaused, so the flag stayed true after the first background cycle (it is only ever set, never reset). A player the user later paused by hand would then be wrongly auto-resumed on the next foreground. Clear it after resuming, mirroring the iOS handler. * fix(android): keep playback state when entering PiP * fix(android): pause playback when the PiP window is closed * fix(android): auto-enter PiP only while the last-played video is playing (match iOS) * fix(ios): re-activate the audio session idempotently on foreground There is no public AVAudioSession `isActive` getter, so the cached flag goes stale once the system deactivates the session out-of-band (e.g. while suspended). Drop the early return in activateAudioSession() — setActive(true) is idempotent when already active — so returning to the foreground reliably re-asserts the session without poking the cached flag. * fix(android): clear auto-enter PiP when the last-played view shouldn't drive it refreshPictureInPictureParams() returned early when the last-played view had PiP disabled or auto-enter off. Because setPictureInPictureParams merges, an auto-enter flag enabled by an earlier view would linger, so the activity could still auto-enter PiP for a view that shouldn't drive it. Always set params explicitly, disabling auto-enter when the last-played view doesn't want it. * fix(android): disable auto-enter PiP when there is no last-played video refreshPictureInPictureParams() returned early when no video had played or the last-played view was gone, so a previously-enabled auto-enter flag could linger on the activity (setPictureInPictureParams merges). Sync the params in that case too by disabling auto-enter. Drop the now-unused VideoView arg from createDisabledPictureInPictureParams(). * fix(android): pause on PiP close regardless of exact stopped lifecycle state Closing the PiP window finishes the activity (CREATED → DESTROYED), so checking the exact CREATED state could miss the pause depending on timing and leave audio playing after dismissal. Pause whenever the fragment is no longer started. * perf(ios): skip setCategory when the audio session config is unchanged configureAudioSession() runs on every audio-session refresh and called setCategory unconditionally. On a real device setCategory is a non-trivial IPC (milliseconds, occasionally hundreds when it actually reconfigures the route). The category, mode and options are readable (unlike the active state), so skip the call when they already match — no behaviour change, just fewer redundant IPCs. * fix(ios): refresh the audio session on play/pause so the mix mode follows playback The mix mode (mix-with-others vs interrupt) and activation were only recomputed on prop/lifecycle changes, never on play/pause. So a video started while another app played audio kept mixing instead of interrupting until an unrelated refresh. Call requestAudioSessionUpdate() on timeControlStatus changes so the session tracks the actual playback state. * fix(ios): don't auto-resume a video paused inside Picture-in-Picture The foreground background-resume only checked wasPlayingInBackground && !isPlaying, so a video paused via the PiP window (which sets rate to 0 without going through our pause path) was wrongly resumed on return. Observe rateDidChangeNotification and cancel the resume intent on a .setRateCalled pause (deliberate — PiP/app/lock screen), while keeping it for .appBackgrounded (system) pauses. Available since iOS 15. * refactor(ios): move PiP-pause resume logic out of the player observer VideoPlayerObserver no longer reaches into VideoManager. It observes rate via rateDidChangeNotification (replacing the KVO of \.rate; same trigger plus the reason) and forwards onRateChanged(rate:reason:). HybridVideoPlayer — the owner of wasPlayingInBackground — clears the background-resume intent on a .setRateCalled pause, next to the other places that clear it. No behaviour change. * fix(ios): deliver the rate-change observer synchronously Reading player.rate from a queue:.main block reads it at async delivery time, not post time, so a rapid pause->play coalesces into one stale read and the .setRateCalled pause that clears the background-resume flag can be missed. queue: nil delivers synchronously on the posting thread, matching the other observers here and the threading of the KVO it replaced. * fix(android): keep auto-pausing non-PiP players when backgrounding into PiP onAppEnterBackground returned early for every player while the activity was in PiP, so a second non-background player kept emitting audio in the background. Skip only the player that drives PiP and auto-pause the rest. If the PiP video is not yet designated (auto-enter can race the designation), pause nothing rather than risk pausing the PiP video. * fix(android): refresh PiP params and cancel auto-pause on playWhenReady changes playWhenReady can change without isPlaying (e.g. pausing while buffering), so the PiP auto-enter flag, which is gated on playWhenReady, went stale when refreshed only from onIsPlayingChanged. A player resuming in the background (media notification) must also cancel a pending auto-pause, otherwise the next foreground force-resumes a video the user left paused.
… workflow (#4924) * chore(github): add issue forms, PR template, funding and issue labeler (#11) - ISSUE_TEMPLATE/bug-report.yml: structured bug report form (placeholders, required env fields, optional video-specific fields) - ISSUE_TEMPLATE/config.yml: disable blank issues; route feature requests to Discussions/Ideas and questions to Discussions/Q&A; surface commercial support, Discord and Slack - PULL_REQUEST_TEMPLATE.md: summary/motivation/changes/platforms/test plan + checklist linking CONTRIBUTING.md - FUNDING.yml: GitHub Sponsors + sdk.thewidlarzgroup.com sponsor link - workflows/issue-labeler.yml: deterministic platform/version/repro labels; auto-close unsupported v5 reports * chore(github): add issue forms, PR template, funding and issue labeler - ISSUE_TEMPLATE/bug-report.yml: structured bug report form (placeholders, required env fields, optional video-specific fields) - ISSUE_TEMPLATE/config.yml: disable blank issues; route feature requests to Discussions/Ideas and questions to Discussions/Q&A; surface commercial support, Discord and Slack - PULL_REQUEST_TEMPLATE.md: summary/motivation/changes/platforms/test plan + checklist linking CONTRIBUTING.md - FUNDING.yml: GitHub Sponsors + sdk.thewidlarzgroup.com sponsor link - workflows/issue-labeler.yml: deterministic platform/version/repro labels; auto-close unsupported v5 reports * chore(github): make media/source type multi-select * chore(github): add subtle icons to issue chooser entries * chore(github): drop trailing periods in bug report template * chore(github): run labeler on fork for testing, strip Prerequisites section from issue body * chore(github): trim issue-labeler comment * chore(github): use Title Case labels (Platform:, Repro Provided/Missing Repro, V5/V6/V7) * chore(github): bump RN version placeholder to 0.86.0 * chore(github): add optional last-working-version and nitro-modules fields to bug report * chore(github): move nitro-modules version next to the version fields * chore(github): make Expo required and move it up with the environment fields * chore(github): use 'react-native version' label for consistency with package names * chore(github): default Expo dropdown to Yes (Expo Dev Client) * chore(github): remove Expo default, keep it required (no preselection) * chore(github): default Expo to Yes (Expo Dev Client) * chore(github): order Expo with Expo Dev Client first so it is the default * feat(github): add issue validation script and stale workflow, replacing inline labeler * fix(github): semver-aware outdated check so v7 prereleases nudge to newest (alpha->beta etc.) * chore(github): add @ts-check + JSDoc types and jsconfig for validate-issue script * refactor(github): type github/context with real typedefs (no any) in validate-issue * refactor(github): type github with real Octokit via @octokit/rest devDep * feat(github): restore commercial support link in the v5 close comment * feat(github): use the issue number as utm_id in the v5 commercial support link * refactor(github): single source for bot labels, simpler label-diff and version parsing * fix(github): tell v5 reporters to open a new issue, not re-open the closed one * fix(github): tolerate '.'/'-' separators in prerelease so malformed versions still flag outdated * chore(github): trim redundant comments in validate-issue * chore(github): restrict issue workflows to the upstream repo * chore(github): use self-documenting 'No Stale' instead of ambiguous 'pinned' for stale exemption * refactor(github): incremental label reconcile with exclusive groups (no setLabels clobber) * fix(github): preserve non-Prerequisites body content; grant contents:read for checkout * chore(github): drop test-only exports and dead BOT_LABELS; only handleIssue is exported * chore(github): trim no-marketing note from header comment * feat(github): acknowledge newly opened issues (adaptive: nudges for repro when missing) * chore(github): soften acknowledgment wording (soon instead of shortly) * refactor(github): one welcome comment on open with stacked outdated/repro nudges * refactor(github): render welcome-comment nudges as a bullet list * refactor(github): rename shadowed body var in welcome comment * fix(github): close v5 and older (majors 1-5), guard against 0.x RN-version misentry * test: temporarily point validate-issue guard at moskalakamil fork (revert before upstream) * Revert "test: temporarily point validate-issue guard at moskalakamil fork" This reverts commit 1d29afc; testing on the fork is done. * fix(github): strip Markdown list markers when parsing multi-select platforms * Revert "fix(github): strip Markdown list markers when parsing multi-select platforms"
* feat(skills): add react-native-video usage skill (v6 & v7, skills.sh / Agent Skills) * feat(skills): real-world patterns, web + lifecycle refs, v6<->v7 migration helper * fix(skills): correct relative links in v6<->v7 migration compare table * fix(skills): accurate navigation/background/PiP lifecycle guidance (web+code verified) * docs(contributing): keep the agent skill in sync with user-facing changes (like docs) * docs(skills): state v7 player keeps audio when its view is detached * docs(skills): frame post-unmount audio persistence as a possible bug to verify * docs(skills): neutral, solution-oriented phrasing (no defect/bug framing) * docs(skills): clarify Issue Booster covers bugs in both your app and the library * docs(skills): suggest Issue Booster after ~3 failed attempts at the same issue * docs(skills): require UTM-tagged markdown links for all TheWidlarzGroup URLs * docs(skills): tag TWG links with utm_medium=ai-skill only (let agents set utm_source) * docs(skills): tag TWG links with utm_medium=ai-skill + utm_campaign=rnv-skill * docs(skills): use a single tag — utm_medium=ai-skill — for TWG links * docs(skills): share TWG links as plain URLs, not markdown links * docs(skills): drop plain-URL instruction, just don't use markdown for links * docs(skills): consistency pass — naming, plain-path links, aligned wording, v7 PiP parity * docs(skills): link the real v6 New Architecture page; drop vague (and on iOS) * docs(skills): refine feed guidance and drop redundant background-pause snippet * docs(skills): clearer feed heading; align Video Feed / react-native-video-feed naming * docs(skills): apply code-verification fixes across v6/v7 references * docs(contributing): remind to update the agent skill in the PR template * docs(readme): link the AI agent skill from Documentation & Examples * docs(skill): list Claude Code/Cursor/Codex, add install command, drop skills.sh branding * docs(skills): drop negative 'lacks' framing for ads/tracks/exclusive-playback * docs(skills): recommend manual pause for feeds (one cross-platform approach) * docs(skills): clarify feed lists bound rows not decoders; gate playback via viewability * docs(skills): honest feed expectations — v7 good for most, TikTok-grade needs more * docs(skills): add video-feeds build guide (general patterns + v7 specifics + honest limits) * docs(skills): feed TikTok-grade — split app-side (prefetch/HLS cache/precache) and backend * docs(skills): clarify prefetch can run natively at app launch (before JS bundle) * docs(skills): drop 'biggest win' framing from prefetch note * docs(skills): reframe feed backend bullet around per-user seen/cache-aware state * docs(skills): drop 'modern' from v6 README DRM plugin note * docs(skills): mark useEvent hook as recommended in v7 events example * docs(skills): contact-intent links use contact=true (open the contact form) * docs(skills): clarify v6 save(), maintained fs libs, version-detect tweaks * docs(skills): prefer blob-util for video downloads, fs for general/drop-in * docs(skills): confirm expo-file-system for Expo downloads; note fs libs for poster/thumbnail caching * docs(skills): download poster/thumbnail alongside video for offline availability
) The Agent Skills validator rejects SKILL.md when the frontmatter description contains XML tags ('SKILL.md description cannot contain XML tags'). The description referenced the v6 component as `<Video>`, which is parsed as an XML tag. Replace both occurrences with `Video` and make the trigger list open-ended.
…nd tell (#5011) Adds .github/DISCUSSION_TEMPLATE/ forms so new discussions are structured, mirroring the issue config.yml routing (feature -> Ideas, question -> Q&A). Kept intentionally light (per how prisma/next.js/theia do it): discussions are conversational, so only the core field is required. In Q&A the version is an optional v6/v7 dropdown, never required, since questions are often general. Auto-applies feature (Ideas) and question (Q&A) labels.
On a newly created discussion, posts a short welcome comment. Covers all categories except Announcements and General (Ideas, Q&A, Show and tell, Polls). Labels are left to the category forms (which already auto-apply feature/question), so the bot does not duplicate them. Fires once on 'created', so no bot-comment marker/dedup is needed (unlike the issue bot, which edits-retriggers). @ts-check typed (Octokit, no any), dependency-free, ad-free, restricted to the upstream repo.
Thanks for opening a PR!
Since this is a volunteer project and is very active, anything you can do to reduce the amount of time needed to review and merge your PR is appreciated.
The following steps will help get your PR merged quickly:
Update the documentation
If you've added new functionality, update the README.md with an entry for your prop or event.
The entry should be inserted in alphabetical order.
Update the changelog
After you open the PR, update the CHANGELOG.md file with an entry pointing to your PR.
Provide an example of how to test the change
If the PR requires special testing setup provide all the relevant instructions and files. This may include a sample video file or URL, configuration, or setup steps.
Focus the PR on only one area
Testing multiple features takes longer than isolated changes and if there is a bug in one feature, prevents the other parts of your PR from getting merged until it gets fixed.
If you're touching multiple different areas that aren't related, break the changes up into multiple PRs.
Describe the changes
Add a note describing what your PR does. If there is a change to the behavior of the code, explain why it needs to be updated.