crowdplaysdk 0.6.1 → 0.6.3
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.
- package/README.md +35 -12
- package/ios/CrowdPlayRNModule.swift +10 -1
- package/ios/wire.rb +20 -5
- package/lib/index.d.ts +9 -1
- package/lib/index.js +1 -1
- package/llms.txt +42 -12
- package/package.json +1 -1
- package/src/index.ts +7 -1
package/README.md
CHANGED
|
@@ -97,7 +97,7 @@ Bare React Native (no Expo):
|
|
|
97
97
|
npm install crowdplaysdk
|
|
98
98
|
cd ios && pod install
|
|
99
99
|
gem install xcodeproj --user-install # one time
|
|
100
|
-
ruby node_modules/crowdplaysdk/ios/wire.rb <YourProjectName>
|
|
100
|
+
ruby ../node_modules/crowdplaysdk/ios/wire.rb <YourProjectName> # from ios/
|
|
101
101
|
```
|
|
102
102
|
|
|
103
103
|
`wire.rb` is idempotent. It adds the compiled CrowdPlaySDK Swift package,
|
|
@@ -107,6 +107,18 @@ keeps explicit Swift modules on for the app target (React Native 0.8x turns
|
|
|
107
107
|
them off project-wide on Xcode 26, which breaks importing the compiled
|
|
108
108
|
engine). There is no CocoaPod of our own, so your Podfile stays untouched.
|
|
109
109
|
|
|
110
|
+
**Updating.** `npm install crowdplaysdk@latest`, then wire again so the
|
|
111
|
+
engine moves with the bridge: Expo, `npx expo prebuild` (or a build that
|
|
112
|
+
runs prebuild; an `ios/` folder committed to git is not re-wired by
|
|
113
|
+
`eas build`, so run prebuild yourself and commit the result); bare React
|
|
114
|
+
Native, re-run `wire.rb`. Since 0.6.3 that also re-pins the engine to the
|
|
115
|
+
version this package ships with. 0.6.2 and older only added the engine
|
|
116
|
+
the first time, so upgrading in place kept the old engine. Before the
|
|
117
|
+
next local build, clean (Xcode: Product > Clean Build Folder): an
|
|
118
|
+
incremental build can reuse Xcode's cached copy of the previous engine's
|
|
119
|
+
interface and fail on new symbols such as `storageWarning`. EAS Build
|
|
120
|
+
starts clean every time.
|
|
121
|
+
|
|
110
122
|
**2. Declare permissions in Info.plist** (bare React Native only; the Expo
|
|
111
123
|
plugin does this for you).
|
|
112
124
|
|
|
@@ -165,6 +177,7 @@ CrowdPlay.addListener('phase', …) // idle / connecting / connected / f
|
|
|
165
177
|
CrowdPlay.addListener('participants', …) // who to render tiles for
|
|
166
178
|
CrowdPlay.addListener('uploads', …) // per-session progress
|
|
167
179
|
CrowdPlay.addListener('warning', …) // micSilent / clipping / crossTalk: show these
|
|
180
|
+
CrowdPlay.addListener('storageWarning', …) // optional: message set = storage low (audio only or stopped)
|
|
168
181
|
CrowdPlay.addListener('recording', …) // error means NOT capturing; offer retryRecording()
|
|
169
182
|
CrowdPlay.addListener('audioRoute', …) // current output device, for the output menu
|
|
170
183
|
CrowdPlay.setAudioOutput('speaker') // or 'automatic' (the connected device)
|
|
@@ -223,29 +236,39 @@ backend reachability and your app key. Every failing check names its own
|
|
|
223
236
|
fix. Then record a one-minute session on a real device and confirm the
|
|
224
237
|
upload finishes.
|
|
225
238
|
|
|
226
|
-
##
|
|
239
|
+
## Checking your sessions
|
|
227
240
|
|
|
228
|
-
Your app key
|
|
241
|
+
Your app key also lists the sessions it recorded, over plain HTTPS: no AWS
|
|
229
242
|
account, no SDK required.
|
|
230
243
|
|
|
231
244
|
```bash
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
curl -H "x-liva-key: $APP_KEY" $BASE/sessions
|
|
235
|
-
curl -H "x-liva-key: $APP_KEY" $BASE/sessions/<sessionId>/files
|
|
245
|
+
curl -H "x-liva-key: $APP_KEY" https://dashboard.crowdplay.ai/sessions
|
|
236
246
|
```
|
|
237
247
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
248
|
+
Each row carries the session id, its state (`uploading`, `ok`, `partial`
|
|
249
|
+
or `failed`), the participants, participant minutes and any quality
|
|
250
|
+
warnings. You only see sessions recorded with your key, and the dashboard
|
|
251
|
+
shows the same list. Use it to confirm that sessions upload and render.
|
|
252
|
+
|
|
253
|
+
The recordings themselves stay in CrowdPlay's storage. There is no
|
|
254
|
+
download endpoint for builders: `GET /sessions/<sessionId>/files` answers
|
|
255
|
+
403 to an app key, and the dashboard does not offer downloads either.
|
|
241
256
|
|
|
242
257
|
## Things to know
|
|
243
258
|
|
|
244
259
|
- **Don't force-quit the app** after a session: iOS pauses background
|
|
245
260
|
transfers until next launch. They resume automatically and nothing is
|
|
246
261
|
lost.
|
|
247
|
-
-
|
|
248
|
-
|
|
262
|
+
- With video, a session uses about 6.8 GB per participant-hour on the phone
|
|
263
|
+
(1080p HEVC at 14 Mbps plus 24-bit audio); audio-only is about 0.5 GB.
|
|
264
|
+
Files are deleted as each one uploads. Video waits for WiFi, so on
|
|
265
|
+
cellular it stays on the phone until then. The SDK never fills the phone
|
|
266
|
+
(0.6.2+, engine 0.5.4): under 2 GB free the recording continues audio
|
|
267
|
+
only, under 500 MB it stops, and the call itself always continues. This
|
|
268
|
+
happens silently: the SDK shows nothing on screen. The `storageWarning`
|
|
269
|
+
event says when it happens, if you want to ask the user to free up space.
|
|
270
|
+
`doctor()` also checks free disk (8 GiB by default); joining never
|
|
271
|
+
refuses for storage.
|
|
249
272
|
- Participants should wear **wired or closed-back headphones**. A
|
|
250
273
|
loudspeaker pipes the far end into the local mic and contaminates the
|
|
251
274
|
track; the SDK surfaces this live through the `warning` event.
|
|
@@ -63,6 +63,15 @@ final class CrowdPlayRNCore {
|
|
|
63
63
|
])
|
|
64
64
|
}.store(in: &cancellables)
|
|
65
65
|
|
|
66
|
+
// Storage (D-148): its own event, not a 'warning' kind, so an app
|
|
67
|
+
// that renders every warning does not start showing it. Sent on
|
|
68
|
+
// every change, since the text moves from audio-only to stopped.
|
|
69
|
+
engine.$storageWarning
|
|
70
|
+
.removeDuplicates()
|
|
71
|
+
.sink { [weak self] message in
|
|
72
|
+
self?.send("crowdplay:storageWarning", ["message": message as Any])
|
|
73
|
+
}.store(in: &cancellables)
|
|
74
|
+
|
|
66
75
|
engine.$uploads
|
|
67
76
|
.combineLatest(engine.$uploadsOnWiFi)
|
|
68
77
|
.sink { [weak self] uploads, onWifi in
|
|
@@ -231,7 +240,7 @@ public final class CrowdPlayRNModule: RCTEventEmitter {
|
|
|
231
240
|
public override static func requiresMainQueueSetup() -> Bool { true }
|
|
232
241
|
|
|
233
242
|
public override func supportedEvents() -> [String]! {
|
|
234
|
-
["crowdplay:phase", "crowdplay:recording", "crowdplay:uploads", "crowdplay:participants", "crowdplay:warning", "crowdplay:audioRoute", "crowdplay:voiceActivity", "crowdplay:caption", "crowdplay:agentToolCall"]
|
|
243
|
+
["crowdplay:phase", "crowdplay:recording", "crowdplay:uploads", "crowdplay:participants", "crowdplay:warning", "crowdplay:storageWarning", "crowdplay:audioRoute", "crowdplay:voiceActivity", "crowdplay:caption", "crowdplay:agentToolCall"]
|
|
235
244
|
}
|
|
236
245
|
|
|
237
246
|
@objc public func startVoiceActivityUpdates() {
|
package/ios/wire.rb
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
# ruby node_modules/crowdplaysdk/ios/wire.rb <YourProjectName>
|
|
7
7
|
#
|
|
8
8
|
# What it does (idempotent; safe to re-run):
|
|
9
|
-
# 1. adds the CrowdPlaySDK Swift package (compiled engine) to the project
|
|
9
|
+
# 1. adds the CrowdPlaySDK Swift package (compiled engine) to the project,
|
|
10
|
+
# or re-pins an existing one to SDK_VERSION (an upgrade)
|
|
10
11
|
# 2. copies the three bridge files into your app group + target
|
|
11
12
|
# 3. creates/points the Swift bridging header at the React headers, raises
|
|
12
13
|
# the app target to iOS 17 and keeps explicit Swift modules ON for it
|
|
@@ -30,7 +31,8 @@ SDK_URL = 'https://github.com/symbiateam/crowdplaysdk'
|
|
|
30
31
|
# D-103 the bridge calls 0.5.0 APIs, and npm 0.5.0 to 0.5.2 shipped with
|
|
31
32
|
# this still at 0.4.0, which broke every install (D-121). publish_rn.sh now
|
|
32
33
|
# compiles the bridge against exactly this release before publishing.
|
|
33
|
-
|
|
34
|
+
# 0.5.4: the storage guard and engine.storageWarning (D-148).
|
|
35
|
+
SDK_VERSION = '0.5.4'
|
|
34
36
|
BRIDGE_FILES = ['CrowdPlayRNModule.swift', 'CrowdPlayRNVideoView.swift', 'CrowdPlayReactNative.m'].freeze
|
|
35
37
|
|
|
36
38
|
project_name = ARGV[0] or abort 'usage: ruby wire.rb <YourProjectName>'
|
|
@@ -43,17 +45,30 @@ target = project.targets.find { |t| t.name == project_name } ||
|
|
|
43
45
|
project.targets.find { |t| t.product_type == 'com.apple.product-type.application' }
|
|
44
46
|
abort 'no application target found' unless target
|
|
45
47
|
|
|
46
|
-
# 1. CrowdPlaySDK Swift package
|
|
47
|
-
|
|
48
|
+
# 1. CrowdPlaySDK Swift package, pinned to exactly SDK_VERSION. An existing
|
|
49
|
+
# reference is re-pinned, never skipped: step 2 always replaces the bridge
|
|
50
|
+
# files, and a new bridge against the old engine does not compile (0.6.2
|
|
51
|
+
# upgraded in place kept engine 0.5.3 and failed on storageWarning, D-151).
|
|
52
|
+
requirement = { 'kind' => 'exactVersion', 'version' => SDK_VERSION }
|
|
53
|
+
existing = project.root_object.package_references.select { |r| r.respond_to?(:repositoryURL) && r.repositoryURL == SDK_URL }
|
|
54
|
+
if existing.empty?
|
|
48
55
|
pkg = project.new(Xcodeproj::Project::Object::XCRemoteSwiftPackageReference)
|
|
49
56
|
pkg.repositoryURL = SDK_URL
|
|
50
|
-
pkg.requirement =
|
|
57
|
+
pkg.requirement = requirement
|
|
51
58
|
project.root_object.package_references << pkg
|
|
52
59
|
dep = project.new(Xcodeproj::Project::Object::XCSwiftPackageProductDependency)
|
|
53
60
|
dep.package = pkg
|
|
54
61
|
dep.product_name = 'CrowdPlaySDK'
|
|
55
62
|
target.package_product_dependencies << dep
|
|
56
63
|
puts "+ CrowdPlaySDK package #{SDK_VERSION}"
|
|
64
|
+
else
|
|
65
|
+
existing.each do |pkg|
|
|
66
|
+
next if pkg.requirement == requirement
|
|
67
|
+
|
|
68
|
+
was = (pkg.requirement || {})['version'] || (pkg.requirement || {})['minimumVersion'] || 'unpinned'
|
|
69
|
+
pkg.requirement = requirement
|
|
70
|
+
puts "+ CrowdPlaySDK package #{was} -> #{SDK_VERSION}"
|
|
71
|
+
end
|
|
57
72
|
end
|
|
58
73
|
|
|
59
74
|
# 2. Bridge files into the app group + target.
|
package/lib/index.d.ts
CHANGED
|
@@ -62,7 +62,7 @@ export interface CrowdPlayConfig {
|
|
|
62
62
|
}
|
|
63
63
|
/** The CrowdPlay Terms of Use and Privacy Notice every participant accepts,
|
|
64
64
|
* as a PDF. CrowdPlayConsentScreen links here; a custom consent UI must too. */
|
|
65
|
-
export declare const CROWDPLAY_TERMS_URL = "https://
|
|
65
|
+
export declare const CROWDPLAY_TERMS_URL = "https://www.crowdplay.ai/legal/consumer-terms";
|
|
66
66
|
/** Proof the participant agreed. Produce it with CrowdPlayConsentScreen, or,
|
|
67
67
|
* if you render your own consent UI, call CrowdPlay.consentText() to show the
|
|
68
68
|
* REQUIRED wording and construct the grant at the moment of agreement. */
|
|
@@ -177,6 +177,14 @@ export interface CrowdPlayEvents {
|
|
|
177
177
|
active: boolean;
|
|
178
178
|
message: string;
|
|
179
179
|
};
|
|
180
|
+
/** This iPhone is running out of storage (D-148): under 2 GB free the
|
|
181
|
+
* recording continues audio only, under 500 MB it stops; the call always
|
|
182
|
+
* continues. `message` is null when storage is fine. The SDK shows
|
|
183
|
+
* nothing on screen; listening is optional. Separate from `warning` so
|
|
184
|
+
* apps that render every warning do not start showing it. */
|
|
185
|
+
storageWarning: {
|
|
186
|
+
message: string | null;
|
|
187
|
+
};
|
|
180
188
|
/** The audio output route: current device by name ("AirPods Pro",
|
|
181
189
|
* "iPhone speaker"), connected candidates, and whether headphones are
|
|
182
190
|
* attached. Drive the call screen's output menu from this (D-079). */
|
package/lib/index.js
CHANGED
|
@@ -26,7 +26,7 @@ exports.useLatestCaption = exports.useVoiceActivity = exports.VoiceReactive = ex
|
|
|
26
26
|
const react_native_1 = require("react-native");
|
|
27
27
|
/** The CrowdPlay Terms of Use and Privacy Notice every participant accepts,
|
|
28
28
|
* as a PDF. CrowdPlayConsentScreen links here; a custom consent UI must too. */
|
|
29
|
-
exports.CROWDPLAY_TERMS_URL = 'https://
|
|
29
|
+
exports.CROWDPLAY_TERMS_URL = 'https://www.crowdplay.ai/legal/consumer-terms';
|
|
30
30
|
function native() {
|
|
31
31
|
const module = react_native_1.NativeModules.CrowdPlayReactNative;
|
|
32
32
|
if (!module) {
|
package/llms.txt
CHANGED
|
@@ -81,6 +81,14 @@ EXPO (managed, prebuild or EAS Build; app builders such as X1):
|
|
|
81
81
|
Xcode project. Do not add those Info.plist keys or any native code
|
|
82
82
|
yourself. Expo SDK 50 or newer.
|
|
83
83
|
3. Rebuild: npx expo prebuild, a development build, or eas build.
|
|
84
|
+
UPDATING the package later: npx expo prebuild again so the plugin
|
|
85
|
+
re-pins the engine to the version the package ships with (0.6.3+). An
|
|
86
|
+
ios/ folder committed to git is not re-wired by eas build on its own:
|
|
87
|
+
run prebuild and commit the result. Then clean before a LOCAL build
|
|
88
|
+
(Xcode: Product > Clean Build Folder); an incremental build can reuse
|
|
89
|
+
the previous engine's cached interface and fail with "has no member
|
|
90
|
+
'$storageWarning'". That error after an update means "clean", never
|
|
91
|
+
"edit the bridge". EAS Build starts clean.
|
|
84
92
|
4. Expo Go and any preview that never ran the plugin cannot load native
|
|
85
93
|
modules: there the consent text, the call and recording fail with a
|
|
86
94
|
clear error and the video view shows a placeholder. That is EXPECTED;
|
|
@@ -92,7 +100,8 @@ BARE REACT NATIVE (no Expo):
|
|
|
92
100
|
2. cd ios && pod install (installs RN's own pods; CrowdPlay has none)
|
|
93
101
|
3. gem install xcodeproj --user-install (one-time; wire.rb needs it
|
|
94
102
|
and says so itself if missing)
|
|
95
|
-
ruby node_modules/crowdplaysdk/ios/wire.rb <YourProjectName>
|
|
103
|
+
ruby ../node_modules/crowdplaysdk/ios/wire.rb <YourProjectName>
|
|
104
|
+
(from ios/, where step 2 left you; wire.rb must run there)
|
|
96
105
|
Idempotent. It: adds the CrowdPlaySDK binary Swift package to the Xcode
|
|
97
106
|
project, copies the three native bridge files into the app target,
|
|
98
107
|
creates/points the Swift bridging header at the React headers, raises
|
|
@@ -100,7 +109,10 @@ BARE REACT NATIVE (no Expo):
|
|
|
100
109
|
sets SWIFT_ENABLE_EXPLICIT_MODULES = YES on the app target (React Native
|
|
101
110
|
0.8x sets it to NO project-wide on Xcode 26, and with implicit modules
|
|
102
111
|
the compiled engine's interface cannot be imported). Rerun it any time;
|
|
103
|
-
it never duplicates
|
|
112
|
+
it never duplicates, and after updating the package it re-pins the
|
|
113
|
+
engine to the version the package ships with (0.6.3+); then Product >
|
|
114
|
+
Clean Build Folder before building. Never undo that setting to fix an
|
|
115
|
+
unrelated error.
|
|
104
116
|
4. Info.plist: NSMicrophoneUsageDescription, NSCameraUsageDescription,
|
|
105
117
|
UIBackgroundModes = [audio]. A missing usage string makes iOS kill
|
|
106
118
|
the app at first mic/camera access (uncatchable).
|
|
@@ -324,22 +336,40 @@ Events (CrowdPlay.addListener):
|
|
|
324
336
|
// mic; crossTalk = loudspeaker leaking the far end into
|
|
325
337
|
// this mic (wear headphones); micPolicy = unexpected
|
|
326
338
|
// input device.
|
|
339
|
+
'storageWarning' { message: string | null }
|
|
340
|
+
// 0.6.2+: non-null = the phone is low on storage: under
|
|
341
|
+
// 2 GB free the recording is audio only, under 500 MB it
|
|
342
|
+
// stopped. The call continues. The SDK shows nothing
|
|
343
|
+
// itself; listening is optional. Kept out of 'warning' so
|
|
344
|
+
// apps that render every warning do not show it.
|
|
327
345
|
|
|
328
|
-
##
|
|
346
|
+
## Checking sessions (no AWS, no SDK, plain HTTPS)
|
|
329
347
|
|
|
330
|
-
The app key
|
|
348
|
+
The app key lists the sessions it recorded, metadata only:
|
|
331
349
|
GET https://dashboard.crowdplay.ai/sessions (x-liva-key header)
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
350
|
+
-> [{sessionId, state ("uploading"|"ok"|"partial"|"failed"),
|
|
351
|
+
participants, participantMinutes, warnings, warningNotes,
|
|
352
|
+
uploadedAt}]
|
|
353
|
+
A key sees only its own app's sessions; `state: "ok"` proves the
|
|
354
|
+
integration works end to end. The dashboard shows the same list.
|
|
355
|
+
|
|
356
|
+
Recordings NEVER leave CrowdPlay's storage. There is no download endpoint
|
|
357
|
+
for builders: GET /sessions/<id>/files answers 403 to an app key and to
|
|
358
|
+
dashboard accounts, and the dashboard has no download button. Do NOT
|
|
359
|
+
build any feature that fetches, plays, shows, or analyses a recording
|
|
360
|
+
after the session; the app has no way to obtain one. During the session
|
|
361
|
+
the app can use the `caption` event (voice-AI transcript lines) and
|
|
362
|
+
`snapshot()` (capture health).
|
|
338
363
|
|
|
339
364
|
## Facts, limits, troubleshooting
|
|
340
365
|
|
|
341
|
-
- iOS 17+, React Native >= 0.71. Storage ~
|
|
342
|
-
|
|
366
|
+
- iOS 17+, React Native >= 0.71. Storage ~6.8 GB per participant-hour with
|
|
367
|
+
video, ~0.5 GB audio-only (deleted as files upload; video waits for WiFi,
|
|
368
|
+
so on cellular it stays on the phone until then). The SDK never fills the
|
|
369
|
+
phone (0.6.2+): under 2 GB free the recording continues audio only, under
|
|
370
|
+
500 MB it stops, the call always continues, silently ('storageWarning'
|
|
371
|
+
says so if you listen). doctor() also checks free disk (8 GiB default);
|
|
372
|
+
join never refuses for storage.
|
|
343
373
|
- Keep the screen awake during sessions (e.g. react-native-keep-awake or
|
|
344
374
|
the idle-timer API); a locked phone suspends capture. Force-quitting
|
|
345
375
|
pauses uploads until next launch (nothing lost).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "crowdplaysdk",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"description": "CrowdPlay lossless conversation capture for React Native (iOS). Studio-grade per-participant recording during live video calls, delivered to CrowdPlay automatically.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
package/src/index.ts
CHANGED
|
@@ -71,7 +71,7 @@ export interface CrowdPlayConfig {
|
|
|
71
71
|
|
|
72
72
|
/** The CrowdPlay Terms of Use and Privacy Notice every participant accepts,
|
|
73
73
|
* as a PDF. CrowdPlayConsentScreen links here; a custom consent UI must too. */
|
|
74
|
-
export const CROWDPLAY_TERMS_URL = 'https://
|
|
74
|
+
export const CROWDPLAY_TERMS_URL = 'https://www.crowdplay.ai/legal/consumer-terms';
|
|
75
75
|
|
|
76
76
|
/** Proof the participant agreed. Produce it with CrowdPlayConsentScreen, or,
|
|
77
77
|
* if you render your own consent UI, call CrowdPlay.consentText() to show the
|
|
@@ -180,6 +180,12 @@ export interface CrowdPlayEvents {
|
|
|
180
180
|
* micSilent (dead mic), clipping (too-hot mic), crossTalk (loudspeaker
|
|
181
181
|
* leaking the far end into this mic), micPolicy (unexpected input). */
|
|
182
182
|
warning: { kind: 'audioStalled' | 'micSilent' | 'clipping' | 'crossTalk' | 'micPolicy'; active: boolean; message: string };
|
|
183
|
+
/** This iPhone is running out of storage (D-148): under 2 GB free the
|
|
184
|
+
* recording continues audio only, under 500 MB it stops; the call always
|
|
185
|
+
* continues. `message` is null when storage is fine. The SDK shows
|
|
186
|
+
* nothing on screen; listening is optional. Separate from `warning` so
|
|
187
|
+
* apps that render every warning do not start showing it. */
|
|
188
|
+
storageWarning: { message: string | null };
|
|
183
189
|
/** The audio output route: current device by name ("AirPods Pro",
|
|
184
190
|
* "iPhone speaker"), connected candidates, and whether headphones are
|
|
185
191
|
* attached. Drive the call screen's output menu from this (D-079). */
|