@npaw/forge-react-native-video 9.7.3 → 10.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,11 +8,45 @@
8
8
  #
9
9
  #
10
10
  # @author: Victor Soto <victor.soto@nicepeopleatwork.com>
11
- # @description: CocoaPods spec for the @npaw/forge-react-native-video glue (Forge AVPlayerAdapter onto react-native-video's plugin API).
11
+ # @description: CocoaPods spec for the @npaw/forge-react-native-video glue (Forge AVPlayerAdapter onto react-native-video's plugin API). Selects the react-native-video generation (ios/rnv6 + pod react-native-video, or ios/rnv7 + pods ReactNativeVideo/NitroModules with C++ interop) from the react-native-video version installed in the consuming app; see generations.json.
12
12
 
13
13
  require "json"
14
14
 
15
15
  package = JSON.parse(File.read(File.join(__dir__, "package.json")))
16
+ generations = JSON.parse(File.read(File.join(__dir__, "generations.json")))["generations"]
17
+
18
+ # Which react-native-video generation is installed in the consuming app?
19
+ # Preferred: node resolution from the CocoaPods installation root (the app's
20
+ # ios/ dir), the same technique react-native-video's own podspec uses to find
21
+ # react-native; it follows hoisted and monorepo layouts. Fallback: the sibling
22
+ # node_modules entry next to this package.
23
+ forge_installed_rnv_version = lambda do
24
+ candidates = []
25
+ root = (Pod::Config.instance.installation_root rescue nil)
26
+ if root
27
+ resolved = `cd "#{root}" && node --print "require.resolve('react-native-video/package.json')" 2>/dev/null`.strip
28
+ candidates << resolved unless resolved.empty?
29
+ end
30
+ candidates << File.expand_path(File.join(__dir__, "..", "react-native-video", "package.json"))
31
+ candidates.each do |path|
32
+ next unless File.exist?(path)
33
+ return JSON.parse(File.read(path))["version"]
34
+ end
35
+ nil
36
+ end
37
+
38
+ rnv_version = forge_installed_rnv_version.call
39
+ rnv_major = rnv_version ? rnv_version.split(".").first.to_i : 6
40
+ generation_key = rnv_major >= 7 ? "rnv7" : "rnv6"
41
+ generation = generations[generation_key]
42
+ generation_dir = File.join(__dir__, generation["iosDir"])
43
+ unless File.directory?(generation_dir)
44
+ raise "[NPAWForge] @npaw/forge-react-native-video #{package['version']} does not ship the " \
45
+ "react-native-video #{rnv_major} glue (installed react-native-video #{rnv_version || 'not found'}). " \
46
+ "Install the major that matches your react-native-video: 6.x -> @npaw/forge-react-native-video@^9 " \
47
+ "(npm tag latest); 7.x -> @npaw/forge-react-native-video@^10 (npm tag rn-video-7)."
48
+ end
49
+ Pod::UI.puts "[NPAWForge] react-native-video #{rnv_version || 'not found'} -> #{generation_key} glue (#{generation['iosDir']})"
16
50
 
17
51
  Pod::Spec.new do |s|
18
52
  s.name = "NPAWForgeReactNativeVideo"
@@ -21,23 +55,33 @@ Pod::Spec.new do |s|
21
55
  s.homepage = "https://npaw.com"
22
56
  s.license = { :type => "Commercial", :text => "Copyright (C) NPAW - All Rights Reserved" }
23
57
  s.authors = { "Victor Soto" => "victor.soto@nicepeopleatwork.com" }
24
- s.platforms = { :ios => "13.0", :tvos => "13.0" }
58
+ # react-native-video 7 (beta) declares iOS only; the v6 generation keeps tvOS.
59
+ s.platforms = generation_key == "rnv7" ? { :ios => "13.0" } : { :ios => "13.0", :tvos => "13.0" }
25
60
  s.source = { :path => "." }
26
61
 
27
- s.source_files = "ios/**/*.{h,m,mm,swift}"
62
+ # The ObjC++ TurboModule shell is shared; the Swift coordinator/plugin pair is
63
+ # per generation (the two upstream plugin APIs cannot coexist in one module).
64
+ s.source_files = ["ios/*.{h,m,mm,swift}", "#{generation['iosDir']}/**/*.{h,m,mm,swift}"]
28
65
  s.swift_version = "5.9"
29
- s.pod_target_xcconfig = {
30
- "DEFINES_MODULE" => "YES",
31
- }
66
+
67
+ xcconfig = { "DEFINES_MODULE" => "YES" }
68
+ if generation_key == "rnv7"
69
+ # ReactNativeVideo is a nitro module built with Swift/C++ interop and its
70
+ # public API exposes C++-backed types (NativeVideoConfig, ...); importing it
71
+ # requires the same interop mode here.
72
+ xcconfig["SWIFT_OBJC_INTEROP_MODE"] = "objcxx"
73
+ xcconfig["CLANG_CXX_LANGUAGE_STANDARD"] = "c++20"
74
+ end
75
+ s.pod_target_xcconfig = xcconfig
32
76
 
33
77
  s.dependency "NPAWForgeReactNative"
34
- s.dependency "react-native-video"
78
+ s.dependency generation["podDependency"]
79
+ (generation["extraPodDependencies"] || []).each { |extra| s.dependency extra }
35
80
 
36
81
  # FORGE_APPLE_CONSUMPTION BEGIN (staged: ForgeApple ships inside the core pod)
37
- s.pod_target_xcconfig = {
38
- "DEFINES_MODULE" => "YES",
82
+ s.pod_target_xcconfig = (s.attributes_hash["pod_target_xcconfig"] || {}).merge({
39
83
  "OTHER_SWIFT_FLAGS" => "$(inherited) -DFORGE_RN_HYDRATED",
40
- }
84
+ })
41
85
  # FORGE_APPLE_CONSUMPTION END
42
86
 
43
87
  if respond_to?(:install_modules_dependencies, true)
package/README.md CHANGED
@@ -7,14 +7,20 @@ manages.
7
7
  Use of this package requires an active NPAW commercial agreement. See
8
8
  [LICENSE](./LICENSE).
9
9
 
10
- ## Install
10
+ ## Pick the major that matches your react-native-video
11
11
 
12
- ```sh
13
- npm install @npaw/forge-react-native @npaw/forge-react-native-video
14
- ```
12
+ One package name, one major per react-native-video generation:
15
13
 
16
- Requires `react-native-video` v6 and React Native 0.75+ with the New
17
- Architecture enabled.
14
+ | Your `react-native-video` | Install | npm tag |
15
+ | --- | --- | --- |
16
+ | 6.x (`>=6.11.0 <7`) | `npm install @npaw/forge-react-native @npaw/forge-react-native-video@^9` | `latest` |
17
+ | 7.x (`>=7.0.0-beta.11 <8`, needs `react-native-nitro-modules`) | `npm install @npaw/forge-react-native @npaw/forge-react-native-video@rn-video-7` | `rn-video-7` |
18
+
19
+ The 6.x major runs on React Native 0.74+ with the New Architecture and
20
+ **bridgeless disabled**. The 7.x major is Fabric-native and runs with the
21
+ React Native 0.76+ defaults (New Architecture, bridgeless on). Upstream
22
+ react-native-video 7 is still a beta: the 7.x major of this package tracks
23
+ `7.0.0-beta.11` and is re-verified when 7.0.0 GA ships.
18
24
 
19
25
  ### iOS
20
26
 
@@ -24,17 +30,23 @@ cd ios && pod install
24
30
 
25
31
  ## Usage
26
32
 
27
- Register the adapter alongside your `Video` component; the adapter attaches to
28
- the underlying player and reports playback to the Forge session created with
29
- [`@npaw/forge-react-native`](https://www.npmjs.com/package/@npaw/forge-react-native).
33
+ The JavaScript API is identical for both majors: request the attach, then
34
+ render the player.
30
35
 
31
36
  ```js
32
- import { NPAWForge } from '@npaw/forge-react-native';
33
- import { attachForgeVideo } from '@npaw/forge-react-native-video';
37
+ import NPAWForge from '@npaw/forge-react-native';
38
+ import { attachReactNativeVideo } from '@npaw/forge-react-native-video';
34
39
 
35
- const video = await attachForgeVideo(forge, { identifier: 'main' });
40
+ const forge = await NPAWForge.create('yourAccountCode', { appName: 'My App' });
41
+ await attachReactNativeVideo(forge); // binds the one live player, or the next one created
36
42
  ```
37
43
 
44
+ With several simultaneous players, tag each source with the
45
+ `X-NPAW-Forge` header and attach by token — react-native-video 6:
46
+ `<Video source={{ uri, headers: { 'X-NPAW-Forge': 'main' } }} />`;
47
+ react-native-video 7: `useVideoPlayer({ uri, headers: { 'X-NPAW-Forge': 'main' } })` —
48
+ then `attachReactNativeVideo(forge, { identifier: 'main', headerToken: 'main' })`.
49
+
38
50
  ## Documentation
39
51
 
40
52
  Integration guides and the API reference are published at
@@ -8,7 +8,7 @@
8
8
  //
9
9
  //
10
10
  // @author: Victor Soto <victor.soto@nicepeopleatwork.com>
11
- // @description: Android library build for the @npaw/forge-react-native-video glue module (Forge Media3 adapter onto react-native-video's plugin API). Pure-Java module so it builds under any host-app Kotlin version.
11
+ // @description: Android library build for the @npaw/forge-react-native-video glue module (Forge Media3 adapter onto react-native-video's plugin API). Pure-Java module so it builds under any host-app Kotlin version. Selects the react-native-video generation source set (src/rnv6 or src/rnv7, see generations.json) from the react-native-video version installed in the host app.
12
12
 
13
13
  def isNewArchitectureEnabled() {
14
14
  return project.hasProperty('newArchEnabled') && project.newArchEnabled == 'true'
@@ -25,11 +25,11 @@ def safeExtGet(prop, fallback) {
25
25
 
26
26
  def forgeCoreVersion = project.hasProperty('NpawForge_forgeCoreVersion')
27
27
  ? project.property('NpawForge_forgeCoreVersion')
28
- : (System.getenv('FORGE_CORE_VERSION') ?: '9.7.3')
28
+ : (System.getenv('FORGE_CORE_VERSION') ?: '9.7.4')
29
29
 
30
30
  def forgeAdapterVersion = project.hasProperty('NpawForge_forgeAdapterVersion')
31
31
  ? project.property('NpawForge_forgeAdapterVersion')
32
- : (System.getenv('FORGE_ADAPTER_VERSION') ?: '9.7.3')
32
+ : (System.getenv('FORGE_ADAPTER_VERSION') ?: '9.7.4')
33
33
 
34
34
  android {
35
35
  namespace 'com.npaw.forge.reactnative.video'
@@ -70,6 +70,59 @@ def forgeCoreRnProject = [
70
70
  ].collect { findProject(it) }.find { it != null }
71
71
 
72
72
  def reactNativeVideoProject = findProject(':react-native-video')
73
+ def nitroModulesProject = findProject(':react-native-nitro-modules')
74
+
75
+ // ---- react-native-video generation selection ----
76
+ // One package, one native source set per react-native-video major (see
77
+ // generations.json). The installed react-native-video decides which one
78
+ // compiles: rn-video 6 exposes RNVExoplayerPlugin, rn-video 7 exposes the
79
+ // nitro-based ReactNativeVideoPlugin, and the two APIs cannot coexist in one
80
+ // compilation. A generation dir that is missing from the installed package
81
+ // major fails loudly here instead of as a wall of unresolved symbols.
82
+ def generationsManifest = new groovy.json.JsonSlurper().parse(file('../generations.json'))
83
+
84
+ def installedReactNativeVideoVersion = {
85
+ def candidates = []
86
+ if (reactNativeVideoProject != null) {
87
+ candidates << new File(reactNativeVideoProject.projectDir, '../package.json')
88
+ }
89
+ try {
90
+ def process = new ProcessBuilder(['node', '--print',
91
+ "require.resolve('react-native-video/package.json', { paths: [process.cwd()] })"])
92
+ .directory(rootProject.projectDir)
93
+ .start()
94
+ def resolved = process.inputStream.text.trim()
95
+ process.waitFor()
96
+ if (process.exitValue() == 0 && resolved) {
97
+ candidates << new File(resolved)
98
+ }
99
+ } catch (Exception ignored) {
100
+ // node is optional here; the autolinked project dir is the primary source.
101
+ }
102
+ for (candidate in candidates) {
103
+ if (candidate.exists()) {
104
+ return new groovy.json.JsonSlurper().parse(candidate).version?.toString()
105
+ }
106
+ }
107
+ return null
108
+ }()
109
+
110
+ def reactNativeVideoMajor = installedReactNativeVideoVersion != null
111
+ ? installedReactNativeVideoVersion.tokenize('.')[0].toInteger()
112
+ : 6
113
+ def generationKey = reactNativeVideoMajor >= 7 ? 'rnv7' : 'rnv6'
114
+ def generation = generationsManifest.generations[generationKey]
115
+ def generationSourceDir = file(generation.androidSourceSet)
116
+ if (!generationSourceDir.isDirectory()) {
117
+ throw new GradleException(
118
+ "@npaw/forge-react-native-video: this package major does not ship the react-native-video " +
119
+ "${reactNativeVideoMajor} glue (installed react-native-video ${installedReactNativeVideoVersion ?: 'not found'}). " +
120
+ "Install the major that matches your react-native-video: 6.x -> @npaw/forge-react-native-video@^9 " +
121
+ "(npm tag latest); 7.x -> @npaw/forge-react-native-video@^10 (npm tag rn-video-7).")
122
+ }
123
+ println "[NPAWForge] react-native-video ${installedReactNativeVideoVersion ?: 'not found'} -> ${generationKey} glue (${generation.androidSourceSet})"
124
+
125
+ android.sourceSets.main.java.srcDirs = ['src/main/java', generation.androidSourceSet]
73
126
 
74
127
  // The Forge media3 adapter declares its player SDK compileOnly (runtime
75
128
  // player detection), and react-native-video provides media3 at the app level;
@@ -93,6 +146,12 @@ dependencies {
93
146
  if (reactNativeVideoProject != null) {
94
147
  compileOnly reactNativeVideoProject
95
148
  }
149
+ // rn-video 7's HybridVideoPlayer extends nitro's HybridObject: javac needs the
150
+ // supertype to resolve members, so the nitro project joins the compile classpath
151
+ // (only present in rn-video 7 apps; the v6 source set never references it).
152
+ if (generationKey == 'rnv7' && nitroModulesProject != null) {
153
+ compileOnly nitroModulesProject
154
+ }
96
155
 
97
156
  testImplementation 'junit:junit:4.13.2'
98
157
  }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Copyright (C) NPAW - All Rights Reserved
3
+ *
4
+ * This source code is protected under international copyright law. All rights
5
+ * reserved and protected by the copyright holders.
6
+ * This file is confidential and only available to authorized individuals with the
7
+ * permission of the copyright holders. If you encounter this file and do not have
8
+ * permission, please contact the copyright holders and delete this file.
9
+ *
10
+ *
11
+ * @author: Victor Soto <victor.soto@nicepeopleatwork.com>
12
+ * @description: Generation-agnostic helper that extracts the X-NPAW-Forge correlation token from a react-native-video request-header map (case-insensitive key match, empty values ignored). Pure Java, JVM-unit-tested; the rn-video 7 module reads headers straight from the public source config and feeds them here.
13
+ */
14
+
15
+ package com.npaw.forge.reactnative.video;
16
+
17
+ import androidx.annotation.Nullable;
18
+ import java.util.Locale;
19
+ import java.util.Map;
20
+
21
+ public final class RNVideoHeaderToken {
22
+
23
+ /** Header customers set on the source for multi-player matching (compared case-insensitively). */
24
+ public static final String HEADER = "x-npaw-forge";
25
+
26
+ private RNVideoHeaderToken() {}
27
+
28
+ /**
29
+ * Returns the token carried by the {@code X-NPAW-Forge} header, or null when the map is null,
30
+ * has no such header (any casing), or the value is empty. When the map carries the header
31
+ * under several casings the first non-empty match in iteration order wins.
32
+ */
33
+ @Nullable
34
+ public static String fromHeaders(@Nullable Map<String, String> headers) {
35
+ if (headers == null) {
36
+ return null;
37
+ }
38
+ for (Map.Entry<String, String> entry : headers.entrySet()) {
39
+ String key = entry.getKey();
40
+ if (key == null || !HEADER.equals(key.toLowerCase(Locale.ROOT))) {
41
+ continue;
42
+ }
43
+ String value = entry.getValue();
44
+ if (value != null && !value.isEmpty()) {
45
+ return value;
46
+ }
47
+ }
48
+ return null;
49
+ }
50
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Copyright (C) NPAW - All Rights Reserved
3
+ *
4
+ * This source code is protected under international copyright law. All rights
5
+ * reserved and protected by the copyright holders.
6
+ * This file is confidential and only available to authorized individuals with the
7
+ * permission of the copyright holders. If you encounter this file and do not have
8
+ * permission, please contact the copyright holders and delete this file.
9
+ *
10
+ *
11
+ * @author: Victor Soto <victor.soto@nicepeopleatwork.com>
12
+ * @description: react-native-video 7 plugin object for Android. Extends the upstream ReactNativeVideoPlugin base class (whose constructor registers it in PluginsRegistry.shared, keyed by id) and forwards player create/destroy notifications, delivered on the main thread, to the Forge video module.
13
+ */
14
+
15
+ package com.npaw.forge.reactnative.video;
16
+
17
+ import androidx.annotation.NonNull;
18
+ import androidx.annotation.Nullable;
19
+ import androidx.annotation.OptIn;
20
+ import androidx.media3.common.util.UnstableApi;
21
+ import com.margelo.nitro.video.HybridVideoPlayer;
22
+ import com.twg.video.core.plugins.PluginsRegistry;
23
+ import com.twg.video.core.plugins.ReactNativeVideoPlugin;
24
+ import java.lang.ref.WeakReference;
25
+
26
+ @OptIn(markerClass = UnstableApi.class)
27
+ final class ForgeRNVideoPlugin extends ReactNativeVideoPlugin {
28
+
29
+ /** Registered id becomes "RNV_Plugin_NPAWForge"; a re-registration replaces the previous entry. */
30
+ static final String PLUGIN_NAME = "NPAWForge";
31
+
32
+ interface Listener {
33
+ void onRnvPlayerCreated(@NonNull HybridVideoPlayer player);
34
+
35
+ /** {@code player} is null when upstream's weak reference was already cleared. */
36
+ void onRnvPlayerDestroyed(@Nullable HybridVideoPlayer player);
37
+ }
38
+
39
+ private final Listener listener;
40
+
41
+ ForgeRNVideoPlugin(@NonNull Listener listener) {
42
+ // The upstream base constructor registers `this` in PluginsRegistry.shared;
43
+ // no callback can arrive before this constructor returns because notifications
44
+ // are only issued from the main thread, where this object is created.
45
+ super(PLUGIN_NAME);
46
+ this.listener = listener;
47
+ }
48
+
49
+ void unregister() {
50
+ PluginsRegistry.Companion.getShared().unregister(this);
51
+ }
52
+
53
+ @Override
54
+ public void onPlayerCreated(@NonNull WeakReference<HybridVideoPlayer> player) {
55
+ HybridVideoPlayer hybrid = player.get();
56
+ if (hybrid != null) {
57
+ listener.onRnvPlayerCreated(hybrid);
58
+ }
59
+ }
60
+
61
+ @Override
62
+ public void onPlayerDestroyed(@NonNull WeakReference<HybridVideoPlayer> player) {
63
+ listener.onRnvPlayerDestroyed(player.get());
64
+ }
65
+ }