@pbvision/partner-sdk 0.1.18 → 0.1.20
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 +24 -1
- package/package.json +1 -1
- package/src/index.js +68 -6
package/README.md
CHANGED
|
@@ -19,6 +19,7 @@ standard length game, so we'll notify your servers when the results are ready.
|
|
|
19
19
|
- [Auto-tagging players](#auto-tagging-players)
|
|
20
20
|
- [Folders](#folders)
|
|
21
21
|
- [Recording with the PB Vision app](#recording-with-the-pb-vision-app)
|
|
22
|
+
- [Reporting a game's score](#reporting-a-games-score)
|
|
22
23
|
- [Video Editors and Viewers](#video-editors-and-viewers)
|
|
23
24
|
- [After Video Processing is Done](#after-video-processing-is-done)
|
|
24
25
|
- [Callback Data](#callback-data)
|
|
@@ -168,6 +169,7 @@ entirely, or provide some or all of these fields:
|
|
|
168
169
|
| `facility` | `string` | Name of the facility where the game was recorded, e.g. `"Cool Club #3 - Barcelona"`. Useful for facility and Court Insight integrations. |
|
|
169
170
|
| `court` | `string` | Court identifier where the game was recorded, e.g. `"11A"`. Useful for facility and Court Insight integrations. |
|
|
170
171
|
| `fid` | `integer` | Folder ID, e.g. from `getOrCreateFolder()` (see [Folders](#folders)). Puts the video in that folder in your partner account's library. A `fid` that isn't one of your folders is refused with a 400 error (`unknown folder`). |
|
|
172
|
+
| `playersFolderPath` | `string[]` | Where the video goes in the PB Vision library of everyone it's shared with: the folder names from the top of their library down, e.g. `['Mon Oct 19 open play']`. 1 to 5 names, each 1 to 200 characters with no leading or trailing whitespace. Folders they don't have are made for them. |
|
|
171
173
|
| `playersForTagging` | `object` | The players to **auto-tag** once processing completes, each by a name, an email, or both (see [Auto-tagging players](#auto-tagging-players)). Honored by all of `uploadVideo()`, `sendVideoUrlToDownload()`, `makeVideoId()`, and `makeRecordingLink()`. |
|
|
172
174
|
|
|
173
175
|
The `facility` and `court` fields are primarily used by facility partners
|
|
@@ -218,7 +220,8 @@ games automatically.
|
|
|
218
220
|
|
|
219
221
|
Folders organize the videos in your partner account's library. To put a new
|
|
220
222
|
video in a folder, pass the folder's ID as the `fid` [metadata](#video-metadata)
|
|
221
|
-
field.
|
|
223
|
+
field. To file it in the libraries of the people it's shared with, pass
|
|
224
|
+
`playersFolderPath`.
|
|
222
225
|
|
|
223
226
|
`getOrCreateFolder()` returns the folder with the name you give (inside
|
|
224
227
|
`parent`, if you pass one), creating it if needed, so it's safe to retry. Use a
|
|
@@ -342,6 +345,26 @@ shows a page for installing it.
|
|
|
342
345
|
**Testing.** Links from the test server (`useProdServer: false`) start with
|
|
343
346
|
`pbvision://record` and need a development build of the PB Vision app.
|
|
344
347
|
|
|
348
|
+
### Reporting a game's score
|
|
349
|
+
|
|
350
|
+
If you know a game's final score, report it with `setGameScore()` and PB
|
|
351
|
+
Vision shows it with the game. Name each team by its role at the game's first
|
|
352
|
+
serve, as in [`playersForTagging`](#auto-tagging-players): the team that served
|
|
353
|
+
the first point, and the team that received it. Give the video by its `vid` or
|
|
354
|
+
by the `nonce` you made it with.
|
|
355
|
+
|
|
356
|
+
```javascript
|
|
357
|
+
await pbv.setGameScore({ nonce: `${eventId}-${gameId}`, server: 11, receiver: 7 });
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
You can report before the video is processed, or even recorded: the game gets
|
|
361
|
+
the score when processing finishes, and the response's `applied` says whether
|
|
362
|
+
it has it yet. Report again to correct a score; the latest report wins. A
|
|
363
|
+
score someone enters in PB Vision itself stands, though: a report never
|
|
364
|
+
replaces it, and `applied` is false. Each team's result is its points, or
|
|
365
|
+
`'won'` or `'lost'` if that is all you know. The score is for a video of one
|
|
366
|
+
game.
|
|
367
|
+
|
|
345
368
|
### Video Editors and Viewers
|
|
346
369
|
|
|
347
370
|
Editors can tag themselves and friends in the video. Viewers have read-only
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -139,13 +139,14 @@ export class PBVision {
|
|
|
139
139
|
* @param {VideoMetadata} [metadata]
|
|
140
140
|
* @returns {VideoUrlToDownloadResponse}
|
|
141
141
|
*/
|
|
142
|
-
async sendVideoUrlToDownload (videoUrl, { userEmails = [], name, desc, gameStartEpoch, facility, court, fid, playersForTagging } = {}) {
|
|
142
|
+
async sendVideoUrlToDownload (videoUrl, { userEmails = [], name, desc, gameStartEpoch, facility, court, fid, playersFolderPath, playersForTagging } = {}) {
|
|
143
143
|
assert(typeof videoUrl === 'string' && videoUrl.startsWith('http'),
|
|
144
144
|
'URL must be a string beginning with http')
|
|
145
145
|
assert(videoUrl.split('?')[0].endsWith('.mp4'), 'video URL must have the .mp4 extension')
|
|
146
|
+
assertFolderPath(playersFolderPath)
|
|
146
147
|
const resp = await this.__callAPI(
|
|
147
148
|
'add_video_by_url',
|
|
148
|
-
{ url: videoUrl, userEmails, name, desc, gameStartEpoch, facility, court, fid, playersForTagging })
|
|
149
|
+
{ url: videoUrl, userEmails, name, desc, gameStartEpoch, facility, court, fid, playersFolderPath, playersForTagging })
|
|
149
150
|
return JSON.parse(resp)
|
|
150
151
|
}
|
|
151
152
|
|
|
@@ -182,6 +183,12 @@ export class PBVision {
|
|
|
182
183
|
* @property {integer} [fid] the ID of the folder in your partner account's
|
|
183
184
|
* library to add this video to (see getOrCreateFolder()). A fid that is
|
|
184
185
|
* not one of your folders is refused.
|
|
186
|
+
* @property {Array<string>} [playersFolderPath] where this video goes in
|
|
187
|
+
* the PB Vision library of everyone it's shared with: the folder names
|
|
188
|
+
* from the top of their library down, e.g. ['Mon Oct 19 open play'] or
|
|
189
|
+
* ['Open play', 'Oct 19']. Folders they don't have are made for them.
|
|
190
|
+
* 1 to 5 names, each 1 to 200 characters with no leading or trailing
|
|
191
|
+
* whitespace.
|
|
185
192
|
* @property {PlayersForTagging} [playersForTagging] the players to tag
|
|
186
193
|
* automatically once processing completes, each by a name, an email, or
|
|
187
194
|
* both. Identify players by their role at the start of the first game.
|
|
@@ -238,9 +245,10 @@ export class PBVision {
|
|
|
238
245
|
* no `vid`, when the account paying for the video can't pay for it; for
|
|
239
246
|
* passthrough partners it is always present.
|
|
240
247
|
*/
|
|
241
|
-
async makeVideoId ({ fileExt = 'mp4', userEmails = [], name, desc, gameStartEpoch, facility, court, fid, playersForTagging, nonce } = {}) {
|
|
248
|
+
async makeVideoId ({ fileExt = 'mp4', userEmails = [], name, desc, gameStartEpoch, facility, court, fid, playersFolderPath, playersForTagging, nonce } = {}) {
|
|
249
|
+
assertFolderPath(playersFolderPath)
|
|
242
250
|
const resp = await this.__callAPI('make_video_id',
|
|
243
|
-
{ platform: getPlatform(), fileExt, userEmails, name, desc, gameStartEpoch, facility, court, fid, playersForTagging, nonce })
|
|
251
|
+
{ platform: getPlatform(), fileExt, userEmails, name, desc, gameStartEpoch, facility, court, fid, playersFolderPath, playersForTagging, nonce })
|
|
244
252
|
const { hasCredits, vid } = JSON.parse(resp)
|
|
245
253
|
const ret = { vid, uid: this.uid }
|
|
246
254
|
if (hasCredits !== undefined) {
|
|
@@ -262,6 +270,8 @@ export class PBVision {
|
|
|
262
270
|
* @property {string} [court] the court where the game is played
|
|
263
271
|
* @property {integer} [fid] the ID of the folder in your partner account's
|
|
264
272
|
* library to add the video to (see getOrCreateFolder())
|
|
273
|
+
* @property {Array<string>} [playersFolderPath] where the video goes in
|
|
274
|
+
* the library of everyone it's shared with (see VideoMetadata)
|
|
265
275
|
* @property {PlayersForTagging} [playersForTagging] the players to tag
|
|
266
276
|
* automatically once processing completes
|
|
267
277
|
* @property {string} [nonce] identifies the game, e.g. your own ID for it:
|
|
@@ -320,7 +330,7 @@ export class PBVision {
|
|
|
320
330
|
*/
|
|
321
331
|
async makeRecordingLink ({
|
|
322
332
|
resolution = '1080p', fps = 30, mayChangeResolution = false, mayChangeFPS = false,
|
|
323
|
-
gameStartEpoch, userEmails, name, desc, facility, court, fid, playersForTagging, nonce
|
|
333
|
+
gameStartEpoch, userEmails, name, desc, facility, court, fid, playersFolderPath, playersForTagging, nonce
|
|
324
334
|
} = {}) {
|
|
325
335
|
assert(gameStartEpoch === undefined,
|
|
326
336
|
'makeRecordingLink() does not take gameStartEpoch: the game\'s time comes from its recording, and a time set now would override it')
|
|
@@ -329,6 +339,7 @@ export class PBVision {
|
|
|
329
339
|
assert(RECORDING_FPS.includes(fps), `fps must be 30 or 60, not ${JSON.stringify(fps)}`)
|
|
330
340
|
assert(typeof mayChangeResolution === 'boolean', 'mayChangeResolution must be a boolean')
|
|
331
341
|
assert(typeof mayChangeFPS === 'boolean', 'mayChangeFPS must be a boolean')
|
|
342
|
+
assertFolderPath(playersFolderPath)
|
|
332
343
|
|
|
333
344
|
// checked before making the video, so a 4K link that PB Vision wouldn't
|
|
334
345
|
// analyze never gets one; only a yes is remembered, so an account that
|
|
@@ -338,7 +349,7 @@ export class PBVision {
|
|
|
338
349
|
}
|
|
339
350
|
|
|
340
351
|
const { vid, uid, hasCredits } = await this.makeVideoId({
|
|
341
|
-
fileExt: 'mp4', userEmails, name, desc, facility, court, fid, playersForTagging, nonce })
|
|
352
|
+
fileExt: 'mp4', userEmails, name, desc, facility, court, fid, playersFolderPath, playersForTagging, nonce })
|
|
342
353
|
if (hasCredits === false) {
|
|
343
354
|
throw new Error('PB Vision did not make a video for this recording link: the account paying for it has no credits available (hasCredits is false)')
|
|
344
355
|
}
|
|
@@ -359,6 +370,51 @@ export class PBVision {
|
|
|
359
370
|
return { vid, uid, url: `${this.recordUrl}?${query}` }
|
|
360
371
|
}
|
|
361
372
|
|
|
373
|
+
/**
|
|
374
|
+
* One team's result in a game: the points it scored, `'won'` or `'lost'`
|
|
375
|
+
* when that is all you know, or `null` when you don't know it.
|
|
376
|
+
*
|
|
377
|
+
* @typedef {number|'won'|'lost'|null} TeamResult
|
|
378
|
+
*/
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* @typedef {Object} SetGameScoreResponse
|
|
382
|
+
* @property {string} vid the video
|
|
383
|
+
* @property {boolean} applied whether the game has the score now. While
|
|
384
|
+
* the video awaits processing, false, and processing gives the game the
|
|
385
|
+
* score when it finishes. After, false means the game keeps the score it
|
|
386
|
+
* has: someone entered it in PB Vision, or PB Vision couldn't tell which
|
|
387
|
+
* team served first
|
|
388
|
+
*/
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Reports the final score of a game you recorded, for a video of one game
|
|
392
|
+
* that your partner account owns. Name each team by its role at the game's
|
|
393
|
+
* first serve, as playersForTagging names the players: the team that
|
|
394
|
+
* served the first point, and the team that received it. You can report
|
|
395
|
+
* before the video is processed, or even recorded. Report again to correct
|
|
396
|
+
* it: the latest report wins, though never over a score someone entered in
|
|
397
|
+
* PB Vision itself.
|
|
398
|
+
*
|
|
399
|
+
* @param {Object} score
|
|
400
|
+
* @param {string} [score.vid] the video; give this or nonce
|
|
401
|
+
* @param {string} [score.nonce] the nonce you made the video with; give
|
|
402
|
+
* this or vid
|
|
403
|
+
* @param {TeamResult} score.server the result of the team that served the
|
|
404
|
+
* game's first point
|
|
405
|
+
* @param {TeamResult} score.receiver the result of the team that received
|
|
406
|
+
* it
|
|
407
|
+
* @returns {SetGameScoreResponse}
|
|
408
|
+
*/
|
|
409
|
+
async setGameScore ({ vid, nonce, server, receiver }) {
|
|
410
|
+
assert((vid === undefined) !== (nonce === undefined), 'give either vid or nonce')
|
|
411
|
+
const isResult = x => x === null || x === 'won' || x === 'lost' || (Number.isInteger(x) && x >= 0)
|
|
412
|
+
assert(isResult(server) && isResult(receiver),
|
|
413
|
+
'each team\'s result must be its points, \'won\', \'lost\' or null')
|
|
414
|
+
const resp = await this.__callAPI('video/score/set', { vid, nonce, server, receiver })
|
|
415
|
+
return JSON.parse(resp)
|
|
416
|
+
}
|
|
417
|
+
|
|
362
418
|
/**
|
|
363
419
|
* Upload a video for processing by the AI.
|
|
364
420
|
*
|
|
@@ -604,6 +660,12 @@ async function startResumableUpload (bucket, objName, numBytesTotal) {
|
|
|
604
660
|
}
|
|
605
661
|
}
|
|
606
662
|
|
|
663
|
+
// playersFolderPath is a list of folder names, not a single name
|
|
664
|
+
function assertFolderPath (playersFolderPath) {
|
|
665
|
+
assert(playersFolderPath === undefined || Array.isArray(playersFolderPath),
|
|
666
|
+
'playersFolderPath must be an array of folder names, e.g. [\'Mon Oct 19 open play\']')
|
|
667
|
+
}
|
|
668
|
+
|
|
607
669
|
// GCS asks clients to retry these, with exponential backoff
|
|
608
670
|
function isTemporaryStatus (status) {
|
|
609
671
|
return status >= 500 || status === 408 || status === 429
|