diff --git a/docusaurus/video/docusaurus/docs/api/_common_/broadcast.mdx b/docusaurus/video/docusaurus/docs/api/_common_/broadcast.mdx index e19a8bef..90967231 100644 --- a/docusaurus/video/docusaurus/docs/api/_common_/broadcast.mdx +++ b/docusaurus/video/docusaurus/docs/api/_common_/broadcast.mdx @@ -17,7 +17,7 @@ call.stopHLSBroadcasting(); ```py call.start_broadcasting() -// to end broadcasting +# to end broadcasting call.stop_broadcasting() ``` diff --git a/docusaurus/video/docusaurus/docs/api/_common_/create-call.mdx b/docusaurus/video/docusaurus/docs/api/_common_/create-call.mdx new file mode 100644 index 00000000..7ede25a1 --- /dev/null +++ b/docusaurus/video/docusaurus/docs/api/_common_/create-call.mdx @@ -0,0 +1,81 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +You can create a call by providing the call type and an ID: + +- The [call type](/api/call_types/builtin) controls which features are enabled and sets up permissions. Call type settings and permissions can be set from API, or using the [Stream Dashboard](https://dashboard.getstream.io/). +- Calls IDs can be reused, which means they can be joined multiple times, so it's possible to set up recurring calls. + +You can optionally restrict call access by providing a list of existing users. + +It's also possible to store any custom data with the call object. + + + + +```js +const callType = 'default'; +const callId = 'my-first-call'; +const call = client.video.call(callType, callId); + +call.create({ data: { created_by_id: 'john' } }); + +// optionally provide additional data +call.create({ + data: { + created_by_id: 'john', + // Call members need to be existing users + members: [{ user_id: 'john', role: 'admin' }, { user_id: 'jack' }], + custom: { + color: 'blue', + }, + }, +}); +``` + + + + +```py +from getstream.models.call_request import CallRequest + +call = client.video.call("default", "id") +response = call.create( + data=CallRequest( + created_by_id="sacha", + ), +) + +# optionally provide additional data +response = call.create( + data=CallRequest( + created_by_id="sacha", + # Call members need to be existing users + members=[ + MemberRequest(user_id: "john", role: "admin"), + MemberRequest(user_id: "jack"), + ], + custom={"color": "blue"}, + ), +) +``` + + + + + +```bash +curl -X POST "https://video.stream-io-api.com/video/call/default/${CALL_ID}?api_key=${API_KEY}" \ +-H "Content-Type: application/json" \ +-H "Authorization: ${JWT_TOKEN}" \ +-d '{ + "data": { + "created_by_id": "sacha@getstream.io", + "settings_override": { "audio": { "access_request_enabled": false } } + }, + "members": [ { "role": "speaker", "user_id": "sacha@getstream.io" } ] +}' +``` + + + diff --git a/docusaurus/video/docusaurus/docs/api/_common_/go_live.mdx b/docusaurus/video/docusaurus/docs/api/_common_/go_live.mdx index 2e560a19..71fc7261 100644 --- a/docusaurus/video/docusaurus/docs/api/_common_/go_live.mdx +++ b/docusaurus/video/docusaurus/docs/api/_common_/go_live.mdx @@ -15,7 +15,7 @@ call.goLive({ start_hls: true, start_recording: true }); ```py -call.go_live() +call.go_live(start_hls=True, start_recording=True) ``` diff --git a/docusaurus/video/docusaurus/docs/api/_common_/manage-call-members.mdx b/docusaurus/video/docusaurus/docs/api/_common_/manage-call-members.mdx new file mode 100644 index 00000000..4370193e --- /dev/null +++ b/docusaurus/video/docusaurus/docs/api/_common_/manage-call-members.mdx @@ -0,0 +1,72 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +You can restrict call access by defining a list of members who are allowed to join the call. Call members need to be existing users. Every call member has a call-level role, [you can configure roles](/api/call_types/permissions/) on the call type. + +Call members can receive [push notifications](/api/call_types/settings/#push-notifications-settings). + + + + +```js +// Call members need to be existing users +call.updateCallMembers({ + // You can add new members + // You can also update the role of existing members + update_members: [{ user_id: 'sara' }, { user_id: 'emily', role: 'admin' }], +}); +``` + + + + +```py +# Call members need to be existing users +# You can also update the role of existing members +call.update_members(update_members=[ + MemberRequest(user_id: "sara"), + MemberRequest(user_id: "emily", role: "admin")]) +``` + + + + +```bash +curl -X PUT "https://video.stream-io-api.com/video/call/default/${CALL_ID}/members?api_key=${API_KEY}" \ +-H "Content-Type: application/json" \ +-H "Authorization: ${JWT_TOKEN}" \ +-d '{ + "update_members": [ + { "user_id": "sara" }, + { "user_id": "emily", "role": "admin" } + ] +}' +``` + + + + +You can also remove call members: + + + + +```js +call.updateCallMembers({ + remove_members: ['sara'], +}); +``` + + + + +```py +call.update_members( + remove_members=[ + MemberRequest(user_id: 'jack', role: 'admin') + ] +) +``` + + + diff --git a/docusaurus/video/docusaurus/docs/api/_common_/rtmp.mdx b/docusaurus/video/docusaurus/docs/api/_common_/rtmp.mdx index 7b9202d8..b197b8b6 100644 --- a/docusaurus/video/docusaurus/docs/api/_common_/rtmp.mdx +++ b/docusaurus/video/docusaurus/docs/api/_common_/rtmp.mdx @@ -1,33 +1,50 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; - ```js -const resp = await call.getOrCreate({ data: { created_by_id: 'john' } }); -// user ID of an existing user +const resp = await call.get(); + +// userId of existing user const userId = 'jane'; +await client.upsertUsers({ + users: { + [userId]: { + id: userId, + }, + }, +}); const token = client.createToken(userId); +const rtmpURL = resp.call.ingress.rtmp.address; +const streamKey = token; -const address = resp.call.ingress.rtmp.address.replace( - '', - token, -); +console.log(rtmpURL, streamKey); ``` ```py -response = call.create(GetOrCreateCallRequest( - data=CallRequest( - created_by_id="sacha", - ), - ),) +resp = call.get() +# user ID of an existing user +user_id = 'jane' +client.upsert_users( + users={ + user_id: {'id': user_id} + } +) + +# the token will be valid for 1 hour +exp = int(time.time()) + 60 * 60 + +token = client.create_token(user_id,expiration=exp) + rtmp_url = response.data().call.ingress.rtmp.address +stream_key = token +print(rtmp_url, stream_key) ``` - \ No newline at end of file + diff --git a/docusaurus/video/docusaurus/docs/api/_common_/update-call.mdx b/docusaurus/video/docusaurus/docs/api/_common_/update-call.mdx new file mode 100644 index 00000000..6a81c2d7 --- /dev/null +++ b/docusaurus/video/docusaurus/docs/api/_common_/update-call.mdx @@ -0,0 +1,55 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Default call settings are inherited from the [call type](/api/call_types/settings/). These settings can be overridden if necessary. + + + + +```js +call.update({ + settings_override: { + audio: { mic_default_on: true, default_device: 'speaker' }, + }, +}); + +// or to update custom data +call.update({ custom: { color: 'red' } }); +``` + + + + +```py +call.update( +settings_override=CallSettingsRequest( + audio=AudioSettingsRequest( + mic_default_on=True, + default_device="speaker", + ), +) +) + +# or to update custom data +call.update(custom= { 'color': 'red' }) +``` + + + + +```bash +curl -X PUT "https://video.stream-io-api.com/video/call/default/${CALL_ID}?api_key=${API_KEY}" \ +-H "Content-Type: application/json" \ +-H "Authorization: ${JWT_TOKEN}" \ +-d '{ + "settings_override": { + "audio": { + "mic_default_on": true + } + } +}' + +``` + + + diff --git a/docusaurus/video/docusaurus/docs/api/basics/authentication.mdx b/docusaurus/video/docusaurus/docs/api/basics/authentication.mdx index da3ae4dc..c24f6192 100644 --- a/docusaurus/video/docusaurus/docs/api/basics/authentication.mdx +++ b/docusaurus/video/docusaurus/docs/api/basics/authentication.mdx @@ -109,7 +109,7 @@ user = UserRequest( users[user.id] = user client.upsert_users(users=users) -// or +# or client.update_users_partial( users= [ { @@ -128,50 +128,35 @@ client.update_users_partial( ## Anonymous users -Anonymous users are users that are not authenticated. It's common to use this for watching a livestream or similar where you aren't authenticated. Anonymous users can be connected using client-side SDKs. +Anonymous users are users that are not authenticated. It's common to use this for watching a livestream or similar where you aren't authenticated. Anonymous users can be connected using client-side SDKs. Anonymous users are not counted toward your MAU. ## Guest users -Guest users are temporary user accounts. You can use it to temporarily give someone a name and image when joining a call. Guest users can aslso be created client-side. +Guest users are temporary user accounts. You can use it to temporarily give someone a name and image when joining a call. Guest users can be created client-side. Guest users are counted towards your MAU usage. - - +## Deactivating and deleting users -```js -const guest: UserObjectRequest = { - id: '', - name: '', - custom: { - color: 'red', - }, -}; +While it is usually safer for data retention to deactivate a user, some use cases require completely deleting a user and their data. -const guest = (await client.createGuest({ user: guest })).user; -``` +Deactivating a user means: - - +- the user can't connect to Stream API +- their data will be retained +- a deactivated user can be reactivated -```py -guest = UserRequest( - id = '', - name= '', - custom= { - "color": 'red', - }, -) +Deleting a user means: -guest = (client.video.create_guest(user=guest)).user -``` +- the user can't connect to Stream API +- their data won't appear in user queries - - - -## Deactivating and deleting users - -While it is usually safer for data retention to deactivate a user, some use cases require completely deleting a user and their data. +Delete has the following opitions: -Once a user has been deleted, it cannot be un-deleted, and the user_id cannot be used again. +| Name | Type | Description | Optional | +| ---------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | +| `user` | Enum (soft, pruning, hard) | - Soft: marks user as deleted and retains all user data.
- Pruning: marks user as deleted and nullifies user information.
- Hard: deletes user completely - this requires hard option for messages and conversation as well. | Yes | +| `conversations` | Enum (soft, hard) | - Soft: marks all conversation channels as deleted (same effect as Delete Channels with 'hard' option disabled).
- Hard: deletes channel and all its data completely including messages (same effect as Delete Channels with 'hard' option enabled). | Yes | +| `messages` | Enum (soft, pruning, hard) | - Soft: marks all user messages as deleted without removing any related message data.
- Pruning: marks all user messages as deleted, nullifies message information and removes some message data such as reactions and flags.
- Hard: deletes messages completely with all related information. | Yes | +| `new_channel_owner_id` | string | Channels owned by hard-deleted users will be transferred to this userID. If you doesn't provide a value, the channel owner will have a system generated ID like `delete-user-8219f6578a7395g` | Yes | @@ -181,7 +166,15 @@ client.deactivateUser({ user_id: '', }); -client.deleteUser({ userId: '' }); +//reactivate +client.reactivateUsers({ + user_ids: [''], +}); + +client.deleteUsers({ user_ids: [''] }); + +//restore +client.restoreUsers({ user_ids: [''] }); ``` @@ -193,7 +186,15 @@ client.deactivate_user( user_id= '', ) -client.delete_user( user_id= '' ) +# reactivate +client.reactivate_users( + user_ids= [''], +) + +client.delete_users( user_ids= [''] ) + +# restore +client.restore_users( user_ids= [''] ) ``` @@ -203,60 +204,66 @@ client.delete_user( user_id= '' ) Stream uses JWT (JSON Web Tokens) to authenticate chat users, enabling them to log in. Knowing whether a user is authorized to perform certain actions is managed separately via a role-based permissions system. Tokens need to be generated server-side. -You can optionally provide - -- Expiration time. By default tokens don't have an expiration date. -- Issued at date, which is necessary if you manually want to revoke tokens. By default, the issued at date is set to the current date. +You can optionally provide an expiration time. By default, tokens are valid for 1 hour. ```js const userId = 'john'; -// exp and iat are optional -// the token will be valid for 1hour +// exp is optional (by default the token is valid for an hour) const exp = Math.round(new Date().getTime() / 1000) + 60 * 60; -const iat = Math.round(new Date().getTime() / 1000); -client.createToken(userId, exp, iat); +client.createToken(userId, exp); ``` ```py +# in this example we use Django +# but you can use any framework you like import time +from django.contrib.auth.decorators import login_required +from django.http import JsonResponse -# user ID -user_id = 'john' +# The user token endpoint is protected with the login_required decorator. +@login_required +def create_user_token(request): + # The 'user_id' is retrieved from the request's user instance. + user_id = request.user.id -# exp and iat are optional -# the token will be valid for 1 hour -exp = int(time.time()) + 60 * 60 -iat = int(time.time()) + # the token will be valid for 1 hour + exp = int(time.time()) + 60 * 60 + # Here client is Stream client and it's called with the 'user_id' and the expiration time. + token = client.create_token(user_id, expiration = exp) + + # The token is then returned in the response. + return JsonResponse({'token': token}) -client.create_token(user_id = user_id, exp = exp, iat = iat) ``` +You need to provide the generated tokens to the client SDKs. Stream SDKs accept a token provider, that can be called to retrieve and renew tokens. You need to implement the token provider in your own application, this is usually an HTTP endpoint. + +Example [token provider for the Node SDK using Express](https://codesandbox.io/p/sandbox/stream-node-token-provider-example-f9cl27). + ## Call tokens -Call tokens contain a list of call IDs. If a user is authenticated with a call token, they can only access the specified calls. +Call tokens contain a list of call IDs. If a user is authenticated with a call token, they can only access the specified calls. They are helpful if you want to limit call access, but you want to avoid managing call members (an example: a pay-pre-view link for livestreams, with hundreds of thousands of expected viewers). ```js const userId = 'john'; -// exp and iat are optional -// the token will be valid for 1hour +// exp is optional (by default the token is valid for an hour) const exp = Math.round(new Date().getTime() / 1000) + 60 * 60; -const iat = Math.round(new Date().getTime() / 1000); const call_cids = ['default:call1', 'livestream:call2']; -client.createCallToken(userId, call_cids, exp, iat); +client.createCallToken(userId, call_cids, exp); ``` @@ -269,17 +276,11 @@ user_id = 'john' # exp and iat are optional, token will be valid for 1 hour exp = int(time.time()) + 60 * 60 -iat = int(time.time()) call_cids = ['default:call1', 'livestream:call2'] -client.create_token(user_id=user_id, exp, iat, call_cids) +client.create_cal_token(user_id=user_id, expiration=exp,call_cids= call_cids) ``` - -## Provisioning token in production - -Your authentication service is responsible for generating tokens for your users. It is highly recommended to always create tokens with an expiration. -All SDKs make it easy to automatically re-fetch tokens from your backend servers with token providers when they expire. diff --git a/docusaurus/video/docusaurus/docs/api/basics/calls.mdx b/docusaurus/video/docusaurus/docs/api/basics/calls.mdx index acc81029..e6995227 100644 --- a/docusaurus/video/docusaurus/docs/api/basics/calls.mdx +++ b/docusaurus/video/docusaurus/docs/api/basics/calls.mdx @@ -7,234 +7,151 @@ title: Calls import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; +import CreateCall from '../_common_/create-call.mdx'; +import CallMembers from '../_common_/manage-call-members.mdx'; +import UpdateCall from '../_common_/update-call.mdx'; +import FilterConditions from '../../../shared/_filter-operators.mdx'; +import CallFilters from '../../../shared/video/_call-filters.mdx'; +import CallMemberFilters from '../../../shared/video/_call-member-filters.mdx'; +import CallSort from '../../../shared/video/_call-sort-fields.mdx'; +import CallMemberSort from '../../../shared/video/_call-member-sort-fields.mdx'; ## Creating calls -You can create a call by providing the call type and an ID: + -- The [call type](call_types/builtin) controls which features are enabled and sets up permissions. -- Calls IDs can be reused, which means they can be joined multiple times, so it's possible to set up recurring calls. +## Updating calls + + -You can optionally restrict call access by providing a list of users. +## Manage call members + + + +## Query calls + +For many video calling, live stream, or audio rooms apps, you'll want to show: + +- Upcoming calls +- Calls that are currently live +- Popular live streams / audio rooms with a link to the recording + +### Examples + +Below you can find a few examples of different queries: + +#### Sorting and pagination ```js -const callType = 'default'; -const callId = 'my-first-call'; -const call = client.video.call(callType, callId); - -call.create({ data: { created_by_id: 'john' } }); -// or -call.getOrCreate({ data: { created_by_id: 'john' } }); - -// optionally provide additional data -call.create({ - data: { - created_by_id: 'john', - members: [{ user_id: 'john', role: 'admin' }, { user_id: 'jack' }], - }, +// default sorting +client.video.queryCalls(); + +// sorting and pagination +const queryCallsReq = { + sort: [{ field: 'starts_at', direction: -1 }], + limit: 2, +}; +response = await client.video.queryCalls(queryCallsReq); + +// loading next page +client.video.queryCalls({ + ...queryCallsReq, + next: response.next, }); ``` + ```py -# create a call -call.create( - data=CallRequest( - created_by_id='john' - ) -) -# create a call with more data -call.create( - data=CallRequest( - created_by_id='john', - members=[ - MemberRequest( - user_id: 'john', - role: 'admin' - ), - MemberRequest( - user_id: 'jack' - ) - ] - ), -) -``` - - +# default sorting +client.query_calls() - +# sorting and pagination +response = client.query_calls( + sort= [SortParamRequest(field: 'starts_at', direction: -1)], + limit=2, +) -```bash -curl -X POST "https://video.stream-io-api.com/video/call/default/${CALL_ID}?api_key=${API_KEY}" \ --H "Content-Type: application/json" \ --H "Authorization: ${JWT_TOKEN}" \ --d '{ - "data": { - "created_by_id": "sacha@getstream.io", - "settings_override": { "audio": { "access_request_enabled": false } } - }, - "members": [ { "role": "speaker", "user_id": "sacha@getstream.io" } ] -}' +# loading next page +client.query_calls( + sort= [SortParamRequest(field: 'starts_at', direction: -1)], + limit=2, + next=response.data().next +) ``` -## Updating calls - -- Most of the call type settings can be overridden on a call level. -- Custom data can also be stored for calls. +#### Query live calls ```js -call.update({ - settings_override: { - audio: { mic_default_on: true, default_device: 'speaker' }, - }, +client.video.queryCalls({ + filter_conditions: { backstage: { $eq: false } }, }); - -// or to update custom data -call.update({ custom: { color: 'red' } }); ``` + ```py -# update call settings -call.update( - settings_override=CallSettingsRequest( - audio=AudioSettingsRequest( - mic_default_on=True, - ), - ), -) - -# update call with custom data -call.update( - custom={'color': 'red'} +client.video.query_calls( + filter_conditions={'backstage': {'$eq': False}} ) ``` - - - -```bash -curl -X PUT "https://video.stream-io-api.com/video/call/default/${CALL_ID}?api_key=${API_KEY}" \ --H "Content-Type: application/json" \ --H "Authorization: ${JWT_TOKEN}" \ --d '{ - "settings_override": { - "audio": { - "mic_default_on": true - } - } -}' - -``` - -## Manage call members - -Call members can be added and removed as necessary. Their roles can also be changed. +#### Query upcoming calls ```js -// update or add call members -call.updateCallMembers({ - update_members: [{ user_id: 'sara' }, { user_id: 'jack', role: 'admin' }], -}); - -// remove call members -call.updateCallMembers({ - remove_members: ['sara'], +const mins30 = 1000 * 60 * 60 * 30; +const inNext30mins = new Date(Date.now() + mins30); +client.video.queryCalls({ + filter_conditions: { + starts_at: { $gt: inNext30mins.toISOString() }, + }, }); ``` + ```py -# update or add call members - -call.update_members( - members=[ - MemberRequest(user_id: 'sara'), - MemberRequest(user_id: 'jack', role: 'admin') - ] +from datetime import datetime, timedelta +min30s = timedelta(minutes=30) +in_next_30_mins = datetime.now() + min30s +client.video.query_calls( + filter_conditions={'starts_at': {'$gt': in_next_30_mins.isoformat()}} ) -# remove call members -# Assuming the updated call members are 'sara' and 'jack' -call.update_members( - members=[ - MemberRequest(user_id: 'jack', role: 'admin') - ] -) -``` - - - - -```bash -curl -X PUT "https://video.stream-io-api.com/video/call/default/${CALL_ID}/members?api_key=${API_KEY}" \ --H "Content-Type: application/json" \ --H "Authorization: ${JWT_TOKEN}" \ --d '{ - "update_members": [ - { "user_id": "sara" }, - { "user_id": "emily", "role": "admin" } - ] -}' ``` -## Query calls - -For many video calling, live stream, or audio rooms apps, you'll want to show: - -- Upcoming calls -- Calls that are currently live -- Popular live streams / audio rooms with a link to the recording - -Below you can find a few examples of different queries: +#### Query ongoing calls ```js -// default sorting -client.video.queryCalls(); - -// sorting and pagination -const queryCallsReq = { - sort: [{ field: 'starts_at', direction: -1 }], - limit: 2, -}; -response = await client.video.queryCalls(queryCallsReq); - -// loading next page client.video.queryCalls({ - ...queryCallsReq, - next: response.next, -}); - -// filtering -client.video.queryCalls({ - filter_conditions: { backstage: { $eq: false } }, + filter_conditions: { ongoing: { $eq: true } }, }); ``` @@ -243,32 +160,23 @@ client.video.queryCalls({ ```py -# default sorting -client.query_calls() - -# sorting and pagination -response = client.query_calls( - sort= [SortParamRequest(field: 'starts_at', direction: -1)], - limit=2, -) - -# loading next page -client.query_calls( - sort= [SortParamRequest(field: 'starts_at', direction: -1)], - limit=2, - next=response.data().next -) - -# filtering -client.query_calls( - filter_conditions={'backstage': {'$eq': False}} +client.video.query_calls( + filter_conditions={'ongoing': {'$eq': True}} ) ``` -Filter expressions support multiple match criteria, and it's also possible to combine filters. For more information, visit the [filter operators](https://getstream.io/chat/docs/node/query_syntax_operators/?language=javascript) guide. +### Sort options + + + +### Filter options + + + + ## Query call members @@ -328,4 +236,12 @@ call.query_members( -Filter expressions support multiple match criteria, and it's also possible to combine filters. For more information, visit the [filter operators](https://getstream.io/chat/docs/node/query_syntax_operators/?language=javascript) guide. +### Sort options + + + +### Filter options + + + + diff --git a/docusaurus/video/docusaurus/docs/api/basics/get_started.mdx b/docusaurus/video/docusaurus/docs/api/basics/get_started.mdx index a56845da..eaf1a01f 100644 --- a/docusaurus/video/docusaurus/docs/api/basics/get_started.mdx +++ b/docusaurus/video/docusaurus/docs/api/basics/get_started.mdx @@ -9,6 +9,13 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import RTMP from '../_common_/rtmp.mdx'; import Broadcast from '../_common_/broadcast.mdx'; +import CreateCall from '../_common_/create-call.mdx'; +import CallMembers from '../_common_/manage-call-members.mdx'; +import UpdateCall from '../_common_/update-call.mdx'; + +Stream powers [Chat Messaging](https://getstream.io/chat/), [Video & Audio](https://getstream.io/video/), and [Activity Feeds](https://getstream.io/activity-feeds/) for billions of global end-users across thousands of different apps. + +Stream’s global edge network ensures a faster and more reliable experience for your video calls and livestreams. Excellent developer experience and docs enable you to build in-app video calling in days. Scale to millions of users and thousands of call participants. For the average Stream integration, the development work focuses on code that executes in the client. However, some tasks must be executed from the server for safety (for example, token generation). @@ -39,17 +46,32 @@ pip install getstream +Something doesn't work? You can open a GitHub issue: + +- [Python SDK](https://github.com/GetStream/stream-py) +- [Node SDK](https://github.com/GetStream/stream-node) + ## Creating client To create a server-side client, you'll need your **API key** and **secret**. Both of them can be found in your [Stream Dashboard](https://dashboard.getstream.io/). +You can optionally pass a timeout for the API requests, the default timeout is 3000ms. + ```js +import { StreamClient } from '@stream-io/node-sdk'; +// or +// const { StreamClient } = require("@stream-io/node-sdk"); + const apiKey = ''; const secret = ''; client = new StreamClient(apiKey, secret); + +// optionally add timeout to API requests +// the default timeout is 3000ms +client = new StreamClient(apiKey, secret, { timeout: 3000 }); ``` @@ -58,13 +80,15 @@ client = new StreamClient(apiKey, secret); ```py from getstream import Stream -client = Stream(api_key="your_api_key", api_secret="your_api_secret") +client = Stream(api_key="your_api_key", api_secret="your_api_secret", timeout=3.0) ``` -## Creating user tokens +## Creating users and user tokens + +To create a user you need to provide an ID, and their role. Optionally you can also specify their name and an image, these fields are recognized and rendered by the default SDK components. It's also possible to add any other custom data. Tokens need to be generated server-side. Typically, you integrate this into the part of your codebase where you log in or register users. The tokens provide a way to authenticate a user or give access to a specific set of video/audio calls. @@ -73,266 +97,55 @@ Tokens need to be generated server-side. Typically, you integrate this into the ```js const userId = 'john'; -// exp and iat are optional -// the token will be valid for 1hour -const exp = Math.round(new Date().getTime() / 1000) + 60 * 60; -const iat = Math.round(new Date().getTime() / 1000); - -client.createToken(userId, exp, iat); -``` - - - - -```py -token = client.create_token("admin-user") -``` - - - - -## Creating a call - -You can create a call by providing the call type and an ID: - -- The [call type](call_types/builtin) controls which features are enabled and sets up permissions. -- Calls IDs can be reused, which means they can be joined multiple times, so it's possible to set up recurring calls. - -You can optionally restrict call access by providing a list of users. - - - - -```js -const callType = 'default'; -const callId = 'my-first-call'; -const call = client.video.call(callType, callId); - -call.create({ data: { created_by_id: 'john' } }); - -// optionally provide additional data -call.create({ - data: { - created_by_id: 'john', - members: [{ user_id: 'john', role: 'admin' }, { user_id: 'jack' }], +const newUser: UserObjectRequest = { + id: userId, + role: 'user', + custom: { + color: 'red', }, -}); -``` - - - - -```py -from getstream.models.call_request import CallRequest - -call = client.video.call("default", "id") -response = call.create( - data=CallRequest( - created_by_id="sacha", - ), -) -``` - - - - - -```bash -curl -X POST "https://video.stream-io-api.com/video/call/default/${CALL_ID}?api_key=${API_KEY}" \ --H "Content-Type: application/json" \ --H "Authorization: ${JWT_TOKEN}" \ --d '{ - "data": { - "created_by_id": "sacha@getstream.io", - "settings_override": { "audio": { "access_request_enabled": false } } + name: 'John', + image: 'link/to/profile/image', +}; +await client.upsertUsers({ + users: { + [newUser.id]: newUser, }, - "members": [ { "role": "speaker", "user_id": "sacha@getstream.io" } ] -}' -``` - - - - -## Adding members to a call - - - - -```js -call.updateCallMembers({ - update_members: [{ user_id: 'sara' }, { user_id: 'emily', role: 'admin' }], }); -``` - - - - -```py -call.update_members(update_members=[ - MemberRequest(user_id: "sara"), - MemberRequest(user_id: "emily", role: "admin")]) -``` - - - - -```bash -curl -X PUT "https://video.stream-io-api.com/video/call/default/${CALL_ID}/members?api_key=${API_KEY}" \ --H "Content-Type: application/json" \ --H "Authorization: ${JWT_TOKEN}" \ --d '{ - "update_members": [ - { "user_id": "sara" }, - { "user_id": "emily", "role": "admin" } - ] -}' -``` - - - -## Updating a call - -- Most of the call type settings can be overridden on a call level. - -- Custom data can also be stored for calls. - - - - -```js -call.update({ - settings_override: { - audio: { mic_default_on: true, default_device: 'speaker' }, - }, -}); +// exp is optional (by default the token is valid for an hour) +const exp = Math.round(new Date().getTime() / 1000) + 60 * 60; -// or to update custom data -call.update({ custom: { color: 'red' } }); +client.createToken(userId, exp); ``` ```py -call.update( -settings_override=CallSettingsRequest( - audio=AudioSettingsRequest( - mic_default_on=True, - default_device="speaker", - ), -) +users = {} +user = UserRequest( +id="john", role="user", custom={"color": "red"}, name="John",image= "link/to/profile/image", ) +users[user.id] = user +client.upsert_users(users=users) -// or to update custom data -call.update(custom: { 'color': 'red' }); -``` - - - - -```bash -curl -X PUT "https://video.stream-io-api.com/video/call/default/${CALL_ID}?api_key=${API_KEY}" \ --H "Content-Type: application/json" \ --H "Authorization: ${JWT_TOKEN}" \ --d '{ - "settings_override": { - "audio": { - "mic_default_on": true - } - } -}' +# the token will be valid for 1 hour +exp = int(time.time()) + 60 * 60 +client.create_token(user_id = "john", expiration = exp) ``` -## Streaming - -Broadcasting serves are a means of transmitting live or pre-recorded content to a wide audience. - -We can choose from two approaches to broadcasting the media: - -- [HLS](https://en.wikipedia.org/wiki/HTTP_Live_Streaming) - slight delay, better buffering -- [WebRTC](https://webrtc.org/) - lower latency, less reliability - -It is up to the integrators to decide, what approach will be used in their apps for the audience to consume the streams. - -For more information see the [Streaming section](streaming/overview/). - -The following example shows how to start and stop HLS broadcasting: - - - -Almost all livestream software and hardware supports RTMPS. Our API supports using third-party software for streaming using RTMPS. For more information, reference the [Streaming section](streaming/overview/). - -The following example shows how to get the RTMP address that you need to provide for the third-party software: - - - -## Recording - -Calls can be recorded for later use. Calls recording can be started/stopped via API calls or configured to start automatically when the first user joins the call. - -For more information, see the [Recordings section](recording/calls/). - -The following example shows how to start and stop recording: - - - - -```js -call.startRecording(); - -// to stop recording -call.stopRecording(); -``` - - - - -```py -call.start_recording() - -// to stop recording -call.stop_recording() -``` - - - - -```bash -curl -X POST "https://video.stream-io-api.com/video/call/default/${CALL_ID}/start_recording?api_key=${API_KEY}" \ --H "Authorization: ${JWT_TOKEN}" -``` - - - - -The following example shows how to query existing recordings: +## Creating a call - - + -```js -call.listRecordings(); -``` +## Call members - - + -```py -recordings = call.list_recordings() -``` - - - - -```bash -curl -X GET "https://video.stream-io-api.com/video/call/default/${CALL_ID}/${SESSION_ID}/recordings?api_key=${API_KEY}" \ --H "Authorization: ${JWT_TOKEN}" -``` +## Updating a call - - + diff --git a/docusaurus/video/docusaurus/docs/api/call_types/geofencing.mdx b/docusaurus/video/docusaurus/docs/api/call_types/geofencing.mdx index 95b4c445..28d98afb 100644 --- a/docusaurus/video/docusaurus/docs/api/call_types/geofencing.mdx +++ b/docusaurus/video/docusaurus/docs/api/call_types/geofencing.mdx @@ -63,7 +63,7 @@ client.video.create_call_type( ), ) -//override settings on call level +# override settings on call level call.create( data = CallRequest( created_by_id= 'john', diff --git a/docusaurus/video/docusaurus/docs/api/call_types/manage-types.mdx b/docusaurus/video/docusaurus/docs/api/call_types/manage-types.mdx index 9af84900..ff8e54bc 100644 --- a/docusaurus/video/docusaurus/docs/api/call_types/manage-types.mdx +++ b/docusaurus/video/docusaurus/docs/api/call_types/manage-types.mdx @@ -26,7 +26,7 @@ client.getCallType({name: 'livestream'}); ```py client.video.list_call_types() -//or +# or client.get_call_type(name= 'livestream') ``` diff --git a/docusaurus/video/docusaurus/docs/api/call_types/settings.mdx b/docusaurus/video/docusaurus/docs/api/call_types/settings.mdx index b5747e1d..9b923205 100644 --- a/docusaurus/video/docusaurus/docs/api/call_types/settings.mdx +++ b/docusaurus/video/docusaurus/docs/api/call_types/settings.mdx @@ -65,7 +65,7 @@ client.video.create_call_type( ), ) -// override settings on call level +# override settings on call level call.create( data=CallRequest( created_by_id= 'john', diff --git a/docusaurus/video/docusaurus/docs/api/moderation/overview.mdx b/docusaurus/video/docusaurus/docs/api/moderation/overview.mdx index f18d9703..c14050fe 100644 --- a/docusaurus/video/docusaurus/docs/api/moderation/overview.mdx +++ b/docusaurus/video/docusaurus/docs/api/moderation/overview.mdx @@ -37,10 +37,10 @@ call.unblockUser({ user_id: 'sara' }); ```py -// Block user +# Block user call.block_user( user_id='sara' ) -// Unblock user +# Unblock user call.unblock_user( user_id= 'sara' ) ``` @@ -95,7 +95,7 @@ call.muteUsers({ ```py -// You can specify which kind of stream(s) to mute +# You can specify which kind of stream(s) to mute call.mute_users( mute_all_users= True, audio=True, diff --git a/docusaurus/video/docusaurus/docs/api/recording/recording_calls.mdx b/docusaurus/video/docusaurus/docs/api/recording/recording_calls.mdx index 530bde6d..9752c808 100644 --- a/docusaurus/video/docusaurus/docs/api/recording/recording_calls.mdx +++ b/docusaurus/video/docusaurus/docs/api/recording/recording_calls.mdx @@ -68,7 +68,7 @@ call.listRecordings(); ```py -call.list_recordings(); +call.list_recordings() ``` diff --git a/docusaurus/video/docusaurus/docs/api/streaming/overview.mdx b/docusaurus/video/docusaurus/docs/api/streaming/overview.mdx index 507755bd..f1051f29 100644 --- a/docusaurus/video/docusaurus/docs/api/streaming/overview.mdx +++ b/docusaurus/video/docusaurus/docs/api/streaming/overview.mdx @@ -13,14 +13,20 @@ import RTMP from '../_common_/rtmp.mdx'; In this section, we are going to explain how you can use Stream to power different livestream use cases. -Stream video allows you to power ultra-low-latency streaming (hundreds of milliseconds). This is made possible by our worldwide edge infrastructure, which supports WebRTC for consuming and sending video. +Stream supports HLS and WebRTC-based livestreams. + +Using WebRTC yields the best user experience: + +- Stream video allows you to power ultra-low-latency streaming (hundreds of milliseconds). This is made possible by our worldwide edge infrastructure, which supports WebRTC for consuming and sending video. +- WebRTC livestreams can be interactive (users can send reactions, messages, etc.). This is not possible with other protocols (such as HLS). + +[Let us know if you wish to use other streaming protocols.](https://github.com/GetStream/protocol/discussions/127) Other important features related to livestream that are discussed in this section: - Multiple streams & co-hosts - RTMP in and WebRTC input - Exporting to HLS -- Recording ## Quickstart @@ -50,11 +56,12 @@ call = client.video.call('livestream', callId) response = call.create( data= CallRequest( created_by_id= 'john', - // You can add multiple hosts if you want to + # You can add multiple hosts if you want to members= [MemberRequest( user_id= 'john', role= 'host' )], ), ) ``` + @@ -71,19 +78,14 @@ All we need to do in this case is call the `GoLive` method on the call object, a For more information, see the [Backstage page](../backstage). -### Test watching the stream +### Sending video from browser or mobile with WebRTC -The Stream API supports two different kinds of streams: +For testing purposes, you can use this [simple example host application](https://codesandbox.io/s/javascript-livestream-host-3hs4vt). You can open the application multiple times which allows you to have multiple hosts, who can send multiple audio/video streams. Don't forget to provide the necessary credentials before testing. -- [WebRTC](../webrtc) -- [HLS](../hls) +### Watching the stream For testing purposes, you can use this [simple example application](https://codesandbox.io/s/javascript-livestream-viewer-lwzgmw) that can play WebRTC and HLS streams as well. Don't forget to provide the necessary credentials before testing. -### Test sending video via WebRTC - -For testing purposes, you can use this [simple example host application](https://codesandbox.io/s/javascript-livestream-host-3hs4vt). You can open the application multiple times which allows you to have multiple hosts, who can send multiple audio/video streams. Don't forget to provide the necessary credentials before testing. - ### Test sending video via RTMP using OBS Almost all livestream software and hardware supports RTMP. Our API supports using third-party software for streaming using RTMP. @@ -105,7 +107,3 @@ Press start streaming in OBS. The RTMP stream will now show up in your call just You can test the livestream with the test application linked above. For more information on this topic, see the [RTMP page](../rtmp). - -## Recording - -Liverstreams (and any other calls) can be recorded for later use. For more information see the [Recording section](../../recording/calls). diff --git a/docusaurus/video/docusaurus/docs/api/streaming/rtmp.mdx b/docusaurus/video/docusaurus/docs/api/streaming/rtmp.mdx index 90637567..38834577 100644 --- a/docusaurus/video/docusaurus/docs/api/streaming/rtmp.mdx +++ b/docusaurus/video/docusaurus/docs/api/streaming/rtmp.mdx @@ -2,16 +2,18 @@ id: rtmp sidebar_position: 3 slug: /streaming/rtmp -title: RTMP +title: RTMP input --- import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -## RTMP support overview +## RTMP input support overview Almost all livestream software and hardware supports RTMP. Our API supports using third-party software for streaming using RTMP. +_Please not that this page is about publishing video/audio using RTMP, **NOT** watching livestreams using RTMP._ + ## RTMP publishing This is how you can acquire the necessary information for publishing RTMP using a third-party software. @@ -21,14 +23,20 @@ This is how you can acquire the necessary information for publishing RTMP using ```js const resp = await call.getOrCreate({ data: { created_by_id: 'john' } }); -// user ID of an existing user +// userId of existing user const userId = 'jane'; +await client.upsertUsers({ + users: { + [userId]: { + id: userId, + }, + }, +}); const token = client.createToken(userId); +const rtmpURL = resp.call.ingress.rtmp.address; +const streamKey = token; -const address = resp.call.ingress.rtmp.address.replace( - '', - token, -); +console.log(rtmpURL, streamKey); ``` @@ -44,10 +52,15 @@ response = call.create( # user ID of an existing user userId = 'jane' -token = client.create_token(userId) -# replace in rtmp address with actual token -address = response.data().call.ingress.rtmp.address.replace('', token) +# the token will be valid for 1 hour +exp = int(time.time()) + 60 * 60 + +token = client.create_token(userId, expiration = exp) + +rtmp_url = response.data().call.ingress.rtmp.address +stream_key = token +print(rtmp_url, stream_key) ```