Skip to content

Commit b1b3db3

Browse files
feat(ios): add overridePlayerAsset to AVPlayerPlugin (#4522)
1 parent 2bdc0da commit b1b3db3

File tree

10 files changed

+196
-81
lines changed

10 files changed

+196
-81
lines changed

docs/pages/other/plugin.md

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -209,29 +209,80 @@ class MyAVPlayerAnalyticsPlugin: RNVAVPlayerPlugin {
209209
override func onInstanceRemoved(id: String, player: AVPlayer) {
210210
// Handle AVPlayer removal with type-safe access
211211
}
212+
213+
/// Optionally override the asset used by the player before playback starts
214+
override func overridePlayerAsset(source: VideoSource, asset: AVAsset) async -> OverridePlayerAssetResult? {
215+
// Return a modified asset or nil to use the default
216+
return nil
217+
}
212218
}
213219
```
214220

215-
The `RNVPlugin` class defines two methods:
221+
The `RNVAVPlayerPlugin` class defines several extension points:
216222

217223
```swift
218224
/**
219-
* Function called when a new player is created
225+
* Function called when a new AVPlayer instance is created
220226
* @param id: a random string identifying the player
221-
* @param player: the instantiated player reference
227+
* @param player: the instantiated AVPlayer
222228
*/
223-
open func onInstanceCreated(id: String, player: Any) { /* no-op */ }
229+
open func onInstanceCreated(id: String, player: AVPlayer) { /* no-op */ }
224230

225231
/**
226-
* Function called when a player should be destroyed
227-
* when this callback is called, the plugin shall free all
228-
* resources and release all reference to Player object
232+
* Function called when an AVPlayer instance is being removed
229233
* @param id: a random string identifying the player
230-
* @param player: the player to release
234+
* @param player: the AVPlayer to release
235+
*/
236+
open func onInstanceRemoved(id: String, player: AVPlayer) { /* no-op */ }
237+
238+
/**
239+
* Optionally override the asset used by the player before playback starts.
240+
* Allows you to modify or replace the AVAsset before it is used to create the AVPlayerItem.
241+
* Return nil to use the default asset.
242+
*
243+
* @param source: The VideoSource describing the video (uri, type, headers, etc.)
244+
* @param asset: The AVAsset prepared by the player
245+
* @return: OverridePlayerAssetResult if you want to override, or nil to use the default
231246
*/
232-
open func onInstanceRemoved(id: String, player: Any) { /* no-op */ }
247+
open func overridePlayerAsset(source: VideoSource, asset: AVAsset) async -> OverridePlayerAssetResult? { nil }
248+
```
249+
250+
##### `OverridePlayerAssetResult` and `OverridePlayerAssetType`
251+
252+
To override the asset, return an `OverridePlayerAssetResult`:
253+
254+
```swift
255+
public struct OverridePlayerAssetResult {
256+
public let type: OverridePlayerAssetType
257+
public let asset: AVAsset
258+
259+
public init(type: OverridePlayerAssetType, asset: AVAsset) {
260+
self.type = type
261+
self.asset = asset
262+
}
263+
}
264+
265+
public enum OverridePlayerAssetType {
266+
case partial // Return a partially modified asset; will go through the default prepare process
267+
case full // Return a fully modified asset; will skip the default prepare process
268+
}
233269
```
234270

271+
- Use `.partial` if you want the asset to continue through the player's normal preparation (e.g., for text tracks or metadata injection).
272+
- Use `.full` if you want to provide a fully prepared asset that will be used as-is for playback.
273+
274+
**Example:**
275+
276+
```swift
277+
override func overridePlayerAsset(source: VideoSource, asset: AVAsset) async -> OverridePlayerAssetResult? {
278+
// Example: Replace the asset URL
279+
let newAsset = AVAsset(url: URL(string: "https://example.com/override.mp4")!)
280+
return Result(type: .full, asset: newAsset)
281+
}
282+
```
283+
284+
> Only one plugin can override the player asset at a time. If multiple plugins implement this, only the first will be used.
285+
235286
### 3. Register the Plugin
236287

237288
To register the plugin in `react-native-video`, call:

examples/expo/ios/Podfile.lock

Lines changed: 67 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
PODS:
22
- boost (1.84.0)
33
- DoubleConversion (1.1.6)
4-
- EXConstants (17.0.3):
4+
- EXConstants (17.0.8):
55
- ExpoModulesCore
6-
- Expo (52.0.7):
6+
- Expo (52.0.46):
77
- ExpoModulesCore
8-
- ExpoAsset (11.0.1):
8+
- ExpoAsset (11.0.5):
99
- ExpoModulesCore
10-
- ExpoFileSystem (18.0.3):
10+
- ExpoFileSystem (18.0.12):
1111
- ExpoModulesCore
12-
- ExpoFont (13.0.1):
12+
- ExpoFont (13.0.4):
1313
- ExpoModulesCore
14-
- ExpoKeepAwake (14.0.1):
14+
- ExpoKeepAwake (14.0.3):
1515
- ExpoModulesCore
16-
- ExpoModulesCore (2.0.3):
16+
- ExpoModulesCore (2.2.3):
1717
- DoubleConversion
1818
- glog
1919
- hermes-engine
@@ -36,7 +36,7 @@ PODS:
3636
- ReactCommon/turbomodule/bridging
3737
- ReactCommon/turbomodule/core
3838
- Yoga
39-
- ExpoSplashScreen (0.29.11):
39+
- ExpoSplashScreen (0.29.24):
4040
- ExpoModulesCore
4141
- FBLazyVector (0.76.2-0)
4242
- fmt (9.1.0)
@@ -1278,7 +1278,7 @@ PODS:
12781278
- ReactCommon/turbomodule/bridging
12791279
- ReactCommon/turbomodule/core
12801280
- Yoga
1281-
- react-native-video (6.10.0):
1281+
- react-native-video (6.12.0):
12821282
- DoubleConversion
12831283
- glog
12841284
- hermes-engine
@@ -1291,7 +1291,7 @@ PODS:
12911291
- React-featureflags
12921292
- React-graphics
12931293
- React-ImageManager
1294-
- react-native-video/Video (= 6.10.0)
1294+
- react-native-video/Video (= 6.12.0)
12951295
- React-NativeModulesApple
12961296
- React-RCTFabric
12971297
- React-rendererdebug
@@ -1322,7 +1322,7 @@ PODS:
13221322
- ReactCommon/turbomodule/bridging
13231323
- ReactCommon/turbomodule/core
13241324
- Yoga
1325-
- react-native-video/Video (6.10.0):
1325+
- react-native-video/Video (6.12.0):
13261326
- DoubleConversion
13271327
- glog
13281328
- hermes-engine
@@ -1851,80 +1851,80 @@ EXTERNAL SOURCES:
18511851
SPEC CHECKSUMS:
18521852
boost: ad4d3a971e4f0b520a127c02c8c3f98b43394c15
18531853
DoubleConversion: 9a8708fb8299350a38a3425761aaea31263b7443
1854-
EXConstants: dd2fe64c6cdb1383b694c309a63028a8e9f2be6d
1855-
Expo: 46cbe74ce0d0f4a4d7b726e90693eb8dfcec6de0
1856-
ExpoAsset: 8138f2a9ec55ae1ad7c3871448379f7d97692d15
1857-
ExpoFileSystem: cc31b7a48031ab565f9eb5c2b61aa08d774a271a
1858-
ExpoFont: 7522d869d84ee2ee8093ee997fef5b86f85d856b
1859-
ExpoKeepAwake: 783e68647b969b210a786047c3daa7b753dcac1f
1860-
ExpoModulesCore: 2d1df04dc27f91d8b383c0ec7f1d121e3d9b7f68
1861-
ExpoSplashScreen: 26cee50e9e95572baf87cd3a8ccaf2ffc3856422
1854+
EXConstants: fcfc75800824ac2d5c592b5bc74130bad17b146b
1855+
Expo: a6ff273c618506b12129a0d06f2a08201bfbcf43
1856+
ExpoAsset: 48386d40d53a8c1738929b3ed509bcad595b5516
1857+
ExpoFileSystem: 42d363d3b96f9afab980dcef60d5657a4443c655
1858+
ExpoFont: f354e926f8feae5e831ec8087f36652b44a0b188
1859+
ExpoKeepAwake: b0171a73665bfcefcfcc311742a72a956e6aa680
1860+
ExpoModulesCore: 15f60d00e33ca0cea27147543877ca6e70c42ef5
1861+
ExpoSplashScreen: 399ee9f85b6c8a61b965e13a1ecff8384db591c2
18621862
FBLazyVector: 6bbb8b88508d32cdf0ffb7ceddedf6aca79eff14
18631863
fmt: a150d37a5595336f19c91e6f1db7fcf17d5eb99f
18641864
glog: 9ab333e271c177cecb6362fe27832c88667cb7e9
18651865
hermes-engine: 7bc33a9a81e6a58b3bc3426105ecd59981ac730e
1866-
RCT-Folly: 2512380c804686400e7f60b9e8b60525df4a6191
1866+
RCT-Folly: 839d900d8e4d2b726f3277dd085d97c8904f9eac
18671867
RCTDeprecation: b90bc991c207fa777f3d4f0d7f36d7473e62319a
18681868
RCTRequired: c8fda281396e4e5cf96e3ab216c05986fb37ca3e
18691869
RCTTypeSafety: 28a29fb79051f043943135d2e4403e77d3b7ec21
18701870
React: dade949d9edcee964714fe90a2892beef3d3a54b
18711871
React-callinvoker: a6517e487e1645d2bc627be8d86b1f8dd5278bb5
1872-
React-Core: 7010fbd0ed122476aa2cf7aa9619ec9ffb73c551
1873-
React-CoreModules: 64eea3d14266e79f8362da71623a5157a7a6c41f
1874-
React-cxxreact: 01f36b93aa3c923f7607c50a43cc2b5a06129f07
1872+
React-Core: 3b252d6ed5c2b9e50ed245f95aa4de1f6b5849ab
1873+
React-CoreModules: 5df4a1fa73a93ca46c33f22148b4f4693b64364e
1874+
React-cxxreact: e8b2b06dfbb9d646d9b45a29c3fc67f230e76fa9
18751875
React-debug: 25139c51b9fda09bb1745952c81cea9f46225d6a
1876-
React-defaultsnativemodule: 3805189f0d7bce05b3650d364676cf9b49e75eab
1877-
React-domnativemodule: abb803da2e62c1acbae61d277fd044fe77f6a9af
1878-
React-Fabric: c8d1c4207248fbaf9c046d92d81bab67d4694122
1879-
React-FabricComponents: 7147628a2fec488771369e0a0dead89ad9649e0e
1880-
React-FabricImage: f814221b18a7d560baaa5fb807895c4c345036be
1876+
React-defaultsnativemodule: 6a217b48ce4eb487e7f0a9697ba6513a274cdbdc
1877+
React-domnativemodule: 64d0ffb108dc00a1275338afdfb78766fbd799d0
1878+
React-Fabric: cb88e6fe3e97be05b425413dbba4fb6cab8e9b34
1879+
React-FabricComponents: c5a6e9c81f9a64521ac8abf9ca2f2532e1398b86
1880+
React-FabricImage: 724ae12ceb53b5f01dfc03d0acd63e392487713f
18811881
React-featureflags: b684bbc804b9c85754fd009167a527a6eadbd9d5
1882-
React-featureflagsnativemodule: 8b29cc7a659d17d0072924a83de9cf9a62a4d147
1883-
React-graphics: 4d7d1fa0ea456c398de7b1f40f708885bccec2e8
1884-
React-hermes: 981bd8b0f89a50a8fd06079bbdc4b11003135021
1885-
React-idlecallbacksnativemodule: 12e7223f085d9d0b82aa0a10b0691294b11c792e
1886-
React-ImageManager: 4300a7a83ee603351659dade2e61e673ae3b88d2
1887-
React-jserrorhandler: 40984dfabb1ab0b2e21d9d21af66530d5309aad0
1888-
React-jsi: ecb93733e8efcd240957abf32b33624ce207d1f9
1889-
React-jsiexecutor: 7ceddf35714b4c4a95ea1f590a16d96b1d577c49
1890-
React-jsinspector: fe8b01493fb3cb8c6b946cb24c3e83e5d774fb1e
1891-
React-jsitracing: 47a37e9a623aadd867574d8143fa314df57f1f49
1892-
React-logger: e928ead07d07fbe16f5bebc7ee004bfb7bf731d5
1893-
React-Mapbuffer: 08b067fc26d19868b0a1fea3586d92afff4e8e4f
1894-
React-microtasksnativemodule: 309d3c6653f4ffcb8a3956604e07ab19b006ef82
1895-
react-native-video: 25ecb15d6ac0f97e2d863cbe317c70bd6e8f1a6e
1896-
react-native-video-plugin-sample: 1f6b1dda047cdf07f6cd7e730dc92c1349656b1a
1882+
React-featureflagsnativemodule: 46d989a753848048218ea29a30c081274c42e661
1883+
React-graphics: accc162849d86b3a8607ce80dd49afc129f35f66
1884+
React-hermes: 2702dda9e6550e7bfcd6d11ed2dd5ac79ee824a7
1885+
React-idlecallbacksnativemodule: c271aad98748669cf8a045b0559a0d3fd67013d2
1886+
React-ImageManager: 6eb6ea557f32850680896907964fc0a8515721db
1887+
React-jserrorhandler: 6236637935a035413e9ada45c7a08a2a58fa7212
1888+
React-jsi: a18137983c0690f3e29882128aa7daf624d2fdac
1889+
React-jsiexecutor: 75909d0eeac14133f33a0446b776388591e1c7c7
1890+
React-jsinspector: db3334448e910b60b59cd1809dcf747970525077
1891+
React-jsitracing: 545fc999f9db67b157323f439ff964caccae2c48
1892+
React-logger: c1804a09cb1c3486fcd9c2b4753ba7b5b9ae1c38
1893+
React-Mapbuffer: 2c717de369c66af62d34cdf52202da3951feab59
1894+
React-microtasksnativemodule: c285dc3fa70ea3099d8ee0887aab0fc53f3ef791
1895+
react-native-video: b18f2dda48b6424cb8d4d6168821223bbf9eb38a
1896+
react-native-video-plugin-sample: 79af31831cddfa916eb264d89b006fdcf227a8f5
18971897
React-nativeconfig: e90353e1c5ab46843222e25d529d342c174184f4
1898-
React-NativeModulesApple: 5f46e9be9fbff00c706cb31a91753975e99f86a1
1899-
React-perflogger: a629f2ce2eeab290cfc851a015c5d804a8b36f20
1900-
React-performancetimeline: 09eaa1261ce7b3d51dcd8001f922c7b7f018957c
1898+
React-NativeModulesApple: de38a9d30fafd773bc72e8b078d57de2dfccae7f
1899+
React-perflogger: 63dd38ee6f413be0aa176de9f6efc65819e289ca
1900+
React-performancetimeline: 450a56eff8fbe3de667f26d1857f3bd150e037a7
19011901
React-RCTActionSheet: 0622ace751a55109844ff5d6239c83ae36f193d6
1902-
React-RCTAnimation: 8651b17476b17d97d7f0fad16d9020f9b1724226
1903-
React-RCTAppDelegate: 09147856b3e6c55f45afc32f485697b9c7097830
1904-
React-RCTBlob: 4ce6e176c0b0999b6ae907537c665dec382095ca
1905-
React-RCTFabric: 6d6d776ec2bb746a599ca113da4c9e5e30519175
1906-
React-RCTImage: 1ba821a26daf160f596266f78b084df38bad43bf
1907-
React-RCTLinking: b913efa56e459a7c9bd8575ea35fffcd43d2b226
1908-
React-RCTNetwork: ff7b971a79b35407aa7fc84d0d34d4bf87977832
1909-
React-RCTSettings: a40d2c591ea4b3ebeb0d5fca84cef83bf3a808b8
1910-
React-RCTText: 54e4c01e54f2b323494950100b3f96a44c8d756a
1911-
React-RCTVibration: 415d77ace6572495a7fabc0013794eb232263f61
1902+
React-RCTAnimation: 90e804d3918a97910c94732f23f1c3d3ac43d1ff
1903+
React-RCTAppDelegate: 7503e65922a7de2dcb04c7ad9dba8d18c0c1084b
1904+
React-RCTBlob: e0ac1d709fab205330a6a4ff3430b19358fe2aa4
1905+
React-RCTFabric: a308ebf92877e49196d81568a0098bc5a54b0cc2
1906+
React-RCTImage: 3ebf257e04f4e0d366e8d11e9ee0c9fb1d5318e4
1907+
React-RCTLinking: 86a8f8fb5169e0499d8ff9f17b466103a9c7344f
1908+
React-RCTNetwork: 9d8dae79abf6281174aabdcd5cb952b125a19834
1909+
React-RCTSettings: 26320c05739c8dc553e87f8195efb51fa1a4f4d1
1910+
React-RCTText: 1b3aafa95d0b8f5546ea8b11f218b97a6c50e6ac
1911+
React-RCTVibration: b4b8c7ca82031b6a628d5197db32123786269f83
19121912
React-rendererconsistency: abd0c952e6447de1851995630d388adb756c5490
1913-
React-rendererdebug: 931afa3e69cad5617d70fc8d22a51b8f0b8962ab
1913+
React-rendererdebug: d2cee4431aae3b94a369922950314bdd11ed4e36
19141914
React-rncore: 4fa27b1f5758f1e43daecaca33828b6724ff975e
1915-
React-RuntimeApple: bc87436ff646bc38b3b40b5c1b64b3292083b5d6
1916-
React-RuntimeCore: 4588a5b4bf68324088d3aba640c7aecda1f8ccf7
1915+
React-RuntimeApple: 4400921398bc7f37dea96b38408f75fa79a4e087
1916+
React-RuntimeCore: f5d8891ca6c8fd0ea1ce97e1e742f674c6e3f3de
19171917
React-runtimeexecutor: 369f2c0dd457ecc25d8aca20717b8b7c655fea30
1918-
React-RuntimeHermes: fa0dbe3e215052f6f2a64961a81292a7d8a29cc1
1919-
React-runtimescheduler: 0c3791a76e2c851a6c0387549f6288cb9dd6e3d6
1918+
React-RuntimeHermes: f36d89a2b787488c5c52b56f6e390ea6daad8177
1919+
React-runtimescheduler: d767ba9dbcc56926ef8e33d9a79ffc73b1f21216
19201920
React-timing: d1b4d886b3ad54cb7d7902551d6f326de16b13d3
1921-
React-utils: 5b35e0df4b1e5414ccb7aad3295de9e422d19265
1922-
ReactCodegen: 64ba3fd7eb98692015dbd29ca0ca38ea2c5f92c6
1923-
ReactCommon: 8676c2ee5eb8c937ea33df7c91f42d4cd9e95e79
1924-
RNCPicker: f963e01f78e546a93b98aa201501713dbda14e94
1921+
React-utils: b5263797499f58d23f8cdf84a43947fa935144a5
1922+
ReactCodegen: be823df68a7af474545f249dab02cd94893917e8
1923+
ReactCommon: ccfca2ddb61b208d3a9bfbbfc87233656d65a4d8
1924+
RNCPicker: 0e683a85cae99a8a739e97a49f269a4630de0a01
19251925
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
1926-
Yoga: aaa386a35c44b356306dafca9e8afd81877fb7ed
1926+
Yoga: 748384d41d07db337d313e0f37eec81c8c0ae46f
19271927

19281928
PODFILE CHECKSUM: 90d803972b4acfc1d2bb6dbe7b0713677a2ff655
19291929

1930-
COCOAPODS: 1.15.2
1930+
COCOAPODS: 1.16.2

ios/Video/DataStructures/AdParams.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
struct AdParams {
1+
public struct AdParams {
22
let adTagUrl: String?
33
let adLanguage: String?
44

ios/Video/DataStructures/CustomMetadata.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
struct CustomMetadata {
1+
public struct CustomMetadata {
22
let title: String?
33
let subtitle: String?
44
let artist: String?
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import AVFoundation
2+
3+
// MARK: - OverridePlayerAssetType
4+
5+
public enum OverridePlayerAssetType {
6+
// Return partially modified asset, that will go through the default prepare process
7+
case partial
8+
// Return fully modified asset, that will skip the default prepare process
9+
case full
10+
}
11+
12+
public typealias OverridePlayerAsset = (VideoSource, AVAsset) async -> OverridePlayerAssetResult
13+
14+
// MARK: - OverridePlayerAssetResult
15+
16+
public struct OverridePlayerAssetResult {
17+
public let type: OverridePlayerAssetType
18+
public let asset: AVAsset
19+
20+
public init(type: OverridePlayerAssetType, asset: AVAsset) {
21+
self.type = type
22+
self.asset = asset
23+
}
24+
}

ios/Video/DataStructures/TextTrack.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
struct TextTrack {
1+
public struct TextTrack {
22
let type: String
33
let language: String
44
let title: String

ios/Video/DataStructures/VideoSource.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
struct VideoSource {
1+
public struct VideoSource {
22
let type: String?
33
let uri: String?
44
let isNetwork: Bool

ios/Video/RCTVideo.swift

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,19 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
493493
])
494494

495495
if let uri = source.uri, uri.starts(with: "ph://") {
496-
let photoAsset = await RCTVideoUtils.preparePHAsset(uri: uri)
496+
guard let photoAsset = await RCTVideoUtils.preparePHAsset(uri: uri) else {
497+
DebugLog("Could not load asset '\(String(describing: _source))'")
498+
throw NSError(domain: "", code: 0, userInfo: nil)
499+
}
500+
501+
if let overridePlayerAsset = await ReactNativeVideoManager.shared.overridePlayerAsset(source: source, asset: photoAsset) {
502+
if overridePlayerAsset.type == .full {
503+
return AVPlayerItem(asset: overridePlayerAsset.asset)
504+
}
505+
506+
return await playerItemPrepareText(source: source, asset: overridePlayerAsset.asset, assetOptions: nil, uri: source.uri ?? "")
507+
}
508+
497509
return await playerItemPrepareText(source: source, asset: photoAsset, assetOptions: nil, uri: source.uri ?? "")
498510
}
499511

@@ -530,6 +542,14 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
530542
)
531543
}
532544

545+
if let overridePlayerAsset = await ReactNativeVideoManager.shared.overridePlayerAsset(source: source, asset: asset) {
546+
if overridePlayerAsset.type == .full {
547+
return AVPlayerItem(asset: overridePlayerAsset.asset)
548+
}
549+
550+
return await playerItemPrepareText(source: source, asset: overridePlayerAsset.asset, assetOptions: assetOptions, uri: source.uri ?? "")
551+
}
552+
533553
return await playerItemPrepareText(source: source, asset: asset, assetOptions: assetOptions, uri: source.uri ?? "")
534554
}
535555

ios/Video/RNVAVPlayerPlugin.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ open class RNVAVPlayerPlugin: RNVPlugin {
3636
*/
3737
open func onInstanceRemoved(id _: String, player _: AVPlayer) { /* no-op */ }
3838

39+
/**
40+
* Function called when creating a new AVPlayerItem
41+
* @param source: The VideoSource describing the video (uri, type, headers, etc.)
42+
* @param asset: The AVAsset prepared by the player
43+
* @return: OverridePlayerAssetResult if you want to override, or nil if you don't
44+
*/
45+
open func overridePlayerAsset(source _: VideoSource, asset _: AVAsset) async -> OverridePlayerAssetResult? { nil }
46+
3947
// MARK: - RNVPlugin methods
4048

4149
override public func onInstanceCreated(id: String, player: Any) {

0 commit comments

Comments
 (0)