Git Product home page Git Product logo

server-sdk-js's Introduction

The LiveKit icon, the name of the repository and some sample code in the background.

LiveKit Server API for JS

Use this SDK to manage LiveKit rooms and create access tokens from your JavaScript/Node.js backend.

Note

This is v2 of the server-sdk-js which runs in NodeJS, Deno and Bun! (It theoretically now also runs in every major browser, but that's not recommended due to the security risks involved with exposing your API secrets) Read the migration section below for a detailed overview on what has changed.

Installation

Pnpm

pnpm add livekit-server-sdk

Yarn

yarn add livekit-server-sdk

NPM

npm install livekit-server-sdk --save

Usage

Environment Variables

You may store credentials in environment variables. If api-key or api-secret is not passed in when creating a RoomServiceClient or AccessToken, the values in the following env vars will be used:

  • LIVEKIT_API_KEY
  • LIVEKIT_API_SECRET

Creating Access Tokens

Creating a token for participant to join a room.

import { AccessToken } from 'livekit-server-sdk';

// if this room doesn't exist, it'll be automatically created when the first
// client joins
const roomName = 'name-of-room';
// identifier to be used for participant.
// it's available as LocalParticipant.identity with livekit-client SDK
const participantName = 'user-name';

const at = new AccessToken('api-key', 'secret-key', {
  identity: participantName,
});
at.addGrant({ roomJoin: true, room: roomName });

const token = await at.toJwt();
console.log('access token', token);

By default, the token expires after 6 hours. you may override this by passing in ttl in the access token options. ttl is expressed in seconds (as number) or a string describing a time span vercel/ms. eg: '2 days', '10h'.

Permissions in Access Tokens

It's possible to customize the permissions of each participant:

const at = new AccessToken('api-key', 'secret-key', {
  identity: participantName,
});

at.addGrant({
  roomJoin: true,
  room: roomName,
  canPublish: false,
  canSubscribe: true,
});

This will allow the participant to subscribe to tracks, but not publish their own to the room.

Managing Rooms

RoomServiceClient gives you APIs to list, create, and delete rooms. It also requires a pair of api key/secret key to operate.

import { RoomServiceClient, Room } from 'livekit-server-sdk';
const livekitHost = 'https://my.livekit.host';
const svc = new RoomServiceClient(livekitHost, 'api-key', 'secret-key');

// list rooms
svc.listRooms().then((rooms: Room[]) => {
  console.log('existing rooms', rooms);
});

// create a new room
const opts = {
  name: 'myroom',
  // timeout in seconds
  emptyTimeout: 10 * 60,
  maxParticipants: 20,
};
svc.createRoom(opts).then((room: Room) => {
  console.log('room created', room);
});

// delete a room
svc.deleteRoom('myroom').then(() => {
  console.log('room deleted');
});

Webhooks

The JS SDK also provides helper functions to decode and verify webhook callbacks. While verification is optional, it ensures the authenticity of the message. See webhooks guide for details.

LiveKit POSTs to webhook endpoints with Content-Type: application/webhook+json. Please ensure your server is able to receive POST body with that MIME.

Check out example projects for full examples of webhooks integration.

import { WebhookReceiver } from 'livekit-server-sdk';

const receiver = new WebhookReceiver('apikey', 'apisecret');

// In order to use the validator, WebhookReceiver must have access to the raw POSTed string (instead of a parsed JSON object)
// if you are using express middleware, ensure that `express.raw` is used for the webhook endpoint
// app.use(express.raw({type: 'application/webhook+json'}));

app.post('/webhook-endpoint', async (req, res) => {
  // event is a WebhookEvent object
  const event = await receiver.receive(req.body, req.get('Authorization'));
});

Migrate from v1.x to v2.x

Token generation

Because the jsonwebtoken lib got replaced with jose, there are a couple of APIs that are now async, that weren't before:

const at = new AccessToken('api-key', 'secret-key', {
  identity: participantName,
});
at.addGrant({ roomJoin: true, room: roomName });

// v1
// const token = at.toJWT();

// v2
const token = await at.toJwt();

// v1
// const grants = v.verify(token);

// v2
const grants = await v.verify(token);

app.post('/webhook-endpoint', async (req, res) => {
  // v1
  // const event = receiver.receive(req.body, req.get('Authorization'));

  // v2
  const event = await receiver.receive(req.body, req.get('Authorization'));
});

Egress API

Egress request types have been updated from interfaces to classes in the latest version. Additionally, oneof fields now require an explicit case field to specify the value type.

For example, to create a RoomComposite Egress:

// v1
// const fileOutput = {
//   fileType: EncodedFileType.MP4,
//   filepath: 'livekit-demo/room-composite-test.mp4',
//   s3: {
//     accessKey: 'aws-access-key',
//     secret: 'aws-access-secret',
//     region: 'aws-region',
//     bucket: 'my-bucket',
//   },
// };

// const info = await egressClient.startRoomCompositeEgress('my-room', {
//   file: fileOutput,
// });

// v2 - current
const fileOutput = new EncodedFileOutput({
  filepath: 'dz/davids-room-test.mp4',
  output: {
    case: 's3',
    value: new S3Upload({
      accessKey: 'aws-access-key',
      secret: 'aws-access-secret',
      bucket: 'my-bucket',
    }),
  },
});

const info = await egressClient.startRoomCompositeEgress('my-room', {
  file: fileOutput,
});


LiveKit Ecosystem
Client SDKsComponents · JavaScript · iOS/macOS · Android · Flutter · React Native · Rust · Python · Unity (web) · Unity (beta)
Server SDKsNode.js · Golang · Ruby · Java/Kotlin · PHP (community) · Python (community)
ServicesLivekit server · Egress · Ingress
ResourcesDocs · Example apps · Cloud · Self-hosting · CLI

server-sdk-js's People

Contributors

biglittlebigben avatar cacheonly avatar davidzhao avatar dennwc avatar dependabot[bot] avatar dsa avatar frostbyte73 avatar github-actions[bot] avatar istorry avatar lukasio avatar m3t avatar moonrailgun avatar neilkinnish avatar noahlt avatar ocupe avatar renovate[bot] avatar shahriar-shojib avatar skaldebane avatar ws avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

server-sdk-js's Issues

deleteRoom doesn't work (throws exception)

when running deleteRoom() it throws an Error: Request failed with status code 401

Here's the full exception
Error: Request failed with status code 401
    at createError (C:\Users\Alex\git\roomService\node_modules\axios\lib\core\createError.js:16:15)
    at settle (C:\Users\Alex\git\roomService\node_modules\axios\lib\core\settle.js:17:12)
    at IncomingMessage.handleStreamEnd (C:\Users\Alex\git\roomService\node_modules\axios\lib\adapters\http.js:269:11)
    at IncomingMessage.emit (events.js:412:35)
    at endReadableNT (internal/streams/readable.js:1317:12)
    at processTicksAndRejections (internal/process/task_queues.js:82:21) {
  config: {
    url: '/twirp/livekit.RoomService/DeleteRoom',
    method: 'post',
    data: '{"room":"L11NOJ9PFWO0KP1P8CLIW"}',
    headers: {
      Accept: 'application/json, text/plain, */*',
      'Content-Type': 'application/json',
      Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2aWRlbyI6eyJyb29tQ3JlYXRlIjp0cnVlfSwiaWF0IjoxNjM3MTI3OTQ4LCJuYmYiOjE2MzcxMjc5NDgsImV4cCI6MTYzNzEyODU0OCwiaXNzIjoiQVBJQ0VDVkpOYXU2NDlWIn0.YEiOV55zjJu3SXfYgPBweakvoeh4ax9g5W3_DwaRbZo',
      'User-Agent': 'axios/0.21.4',
      'Content-Length': 32
    },
    baseURL: 'http://myhost.mydomain:7880',
    transformRequest: [ [Function: transformRequest] ],
    transformResponse: [ [Function: transformResponse] ],
    timeout: 0,
    adapter: [Function: httpAdapter],
    xsrfCookieName: 'XSRF-TOKEN',
    xsrfHeaderName: 'X-XSRF-TOKEN',
    maxContentLength: -1,
    maxBodyLength: -1,
    validateStatus: [Function: validateStatus],
    transitional: {
      silentJSONParsing: true,
      forcedJSONParsing: true,
      clarifyTimeoutError: false
    }
  },
  request: <ref *1> ClientRequest {
    _events: [Object: null prototype] {
      abort: [Function (anonymous)],
      aborted: [Function (anonymous)],
      connect: [Function (anonymous)],
      error: [Function (anonymous)],
      socket: [Function (anonymous)],
      timeout: [Function (anonymous)],
      prefinish: [Function: requestOnPrefinish]
    },
    _eventsCount: 7,
    _maxListeners: undefined,
    outputData: [],
    outputSize: 0,
    writable: true,
    destroyed: false,
    _last: true,
    chunkedEncoding: false,
    shouldKeepAlive: false,
    _defaultKeepAlive: true,
    useChunkedEncodingByDefault: true,
    sendDate: false,
    _removedConnection: false,
    _removedContLen: false,
    _removedTE: false,
    _contentLength: null,
    _hasBody: true,
    _trailer: '',
    finished: true,
    _headerSent: true,
    socket: Socket {
      connecting: false,
      _hadError: false,
      _parent: null,
      _host: 'myhost.mydomain',
      _readableState: [ReadableState],
      _events: [Object: null prototype],
      _eventsCount: 7,
      _maxListeners: undefined,
      _writableState: [WritableState],
      allowHalfOpen: false,
      _sockname: null,
      _pendingData: null,
      _pendingEncoding: '',
      server: null,
      _server: null,
      parser: null,
      _httpMessage: [Circular *1],
      [Symbol(async_id_symbol)]: 23,
      [Symbol(kHandle)]: [TCP],
      [Symbol(kSetNoDelay)]: false,
      [Symbol(lastWriteQueueSize)]: 0,
      [Symbol(timeout)]: null,
      [Symbol(kBuffer)]: null,
      [Symbol(kBufferCb)]: null,
      [Symbol(kBufferGen)]: null,
      [Symbol(kCapture)]: false,
      [Symbol(kBytesRead)]: 0,
      [Symbol(kBytesWritten)]: 0,
      [Symbol(RequestTimeout)]: undefined
    },
    _header: 'POST /twirp/livekit.RoomService/DeleteRoom HTTP/1.1\r\n' +
      'Accept: application/json, text/plain, */*\r\n' +
      'Content-Type: application/json\r\n' +
      'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2aWRlbyI6eyJyb29tQ3JlYXRlIjp0cnVlfSwiaWF0IjoxNjM3MTI3OTQ4LCJuYmYiOjE2MzcxMjc5NDgsImV4cCI6MTYzNzEyODU0OCwiaXNzIjoiQVBJQ0VDVkpOYXU2NDlWIn0.YEiOV55zjJu3SXfYgPBweakvoeh4ax9g5W3_DwaRbZo\r\n' +
      'User-Agent: axios/0.21.4\r\n' +
      'Content-Length: 32\r\n' +
      'Host: myhost.mydomain:7880\r\n' +
      'Connection: close\r\n' +
      '\r\n',
    _keepAliveTimeout: 0,
    _onPendingData: [Function: noopPendingOutput],
    agent: Agent {
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      defaultPort: 80,
      protocol: 'http:',
      options: [Object],
      requests: {},
      sockets: [Object],
      freeSockets: {},
      keepAliveMsecs: 1000,
      keepAlive: false,
      maxSockets: Infinity,
      maxFreeSockets: 256,
      scheduling: 'lifo',
      maxTotalSockets: Infinity,
      totalSocketCount: 1,
      [Symbol(kCapture)]: false
    },
    socketPath: undefined,
    method: 'POST',
    maxHeaderSize: undefined,
    insecureHTTPParser: undefined,
    path: '/twirp/livekit.RoomService/DeleteRoom',
    _ended: true,
    res: IncomingMessage {
      _readableState: [ReadableState],
      _events: [Object: null prototype],
      _eventsCount: 3,
      _maxListeners: undefined,
      socket: [Socket],
      httpVersionMajor: 1,
      httpVersionMinor: 1,
      httpVersion: '1.1',
      complete: true,
      headers: [Object],
      rawHeaders: [Array],
      trailers: {},
      rawTrailers: [],
      aborted: false,
      upgrade: false,
      url: '',
      method: null,
      statusCode: 401,
      statusMessage: 'Unauthorized',
      client: [Socket],
      _consuming: false,
      _dumped: false,
      req: [Circular *1],
      responseUrl: 'http://myhost.mydomain:7880/twirp/livekit.RoomService/DeleteRoom',
      redirects: [],
      [Symbol(kCapture)]: false,
      [Symbol(RequestTimeout)]: undefined
    },
    aborted: false,
    timeoutCb: null,
    upgradeOrConnect: false,
    parser: null,
    maxHeadersCount: null,
    reusedSocket: false,
    host: 'myhost.mydomain',
    protocol: 'http:',
    _redirectable: Writable {
      _writableState: [WritableState],
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      _options: [Object],
      _ended: true,
      _ending: true,
      _redirectCount: 0,
      _redirects: [],
      _requestBodyLength: 32,
      _requestBodyBuffers: [],
      _onNativeResponse: [Function (anonymous)],
      _currentRequest: [Circular *1],
      _currentUrl: 'http://myhost.mydomain:7880/twirp/livekit.RoomService/DeleteRoom',
      [Symbol(kCapture)]: false
    },
    [Symbol(kCapture)]: false,
    [Symbol(kNeedDrain)]: false,
    [Symbol(corked)]: 0,
    [Symbol(kOutHeaders)]: [Object: null prototype] {
      accept: [Array],
      'content-type': [Array],
      authorization: [Array],
      'user-agent': [Array],
      'content-length': [Array],
      host: [Array]
    }
  },
  response: {
    status: 401,
    statusText: 'Unauthorized',
    headers: {
      'content-length': '53',
      'content-type': 'application/json',
      date: 'Wed, 17 Nov 2021 05:47:14 GMT',
      connection: 'close'
    },
    config: {
      url: '/twirp/livekit.RoomService/DeleteRoom',
      method: 'post',
      data: '{"room":"L11NOJ9PFWO0KP1P8CLIW"}',
      headers: [Object],
      baseURL: 'http://myhost.mydomain:7880',
      transformRequest: [Array],
      transformResponse: [Array],
      timeout: 0,
      adapter: [Function: httpAdapter],
      xsrfCookieName: 'XSRF-TOKEN',
      xsrfHeaderName: 'X-XSRF-TOKEN',
      maxContentLength: -1,
      maxBodyLength: -1,
      validateStatus: [Function: validateStatus],
      transitional: [Object]
    },
    request: <ref *1> ClientRequest {
      _events: [Object: null prototype],
      _eventsCount: 7,
      _maxListeners: undefined,
      outputData: [],
      outputSize: 0,
      writable: true,
      destroyed: false,
      _last: true,
      chunkedEncoding: false,
      shouldKeepAlive: false,
      _defaultKeepAlive: true,
      useChunkedEncodingByDefault: true,
      sendDate: false,
      _removedConnection: false,
      _removedContLen: false,
      _removedTE: false,
      _contentLength: null,
      _hasBody: true,
      _trailer: '',
      finished: true,
      _headerSent: true,
      socket: [Socket],
      _header: 'POST /twirp/livekit.RoomService/DeleteRoom HTTP/1.1\r\n' +
        'Accept: application/json, text/plain, */*\r\n' +
        'Content-Type: application/json\r\n' +
        'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2aWRlbyI6eyJyb29tQ3JlYXRlIjp0cnVlfSwiaWF0IjoxNjM3MTI3OTQ4LCJuYmYiOjE2MzcxMjc5NDgsImV4cCI6MTYzNzEyODU0OCwiaXNzIjoiQVBJQ0VDVkpOYXU2NDlWIn0.YEiOV55zjJu3SXfYgPBweakvoeh4ax9g5W3_DwaRbZo\r\n' +
        'User-Agent: axios/0.21.4\r\n' +
        'Content-Length: 32\r\n' +
        'Host: myhost.mydomain:7880\r\n' +
        'Connection: close\r\n' +
        '\r\n',
      _keepAliveTimeout: 0,
      _onPendingData: [Function: noopPendingOutput],
      agent: [Agent],
      socketPath: undefined,
      method: 'POST',
      maxHeaderSize: undefined,
      insecureHTTPParser: undefined,
      path: '/twirp/livekit.RoomService/DeleteRoom',
      _ended: true,
      res: [IncomingMessage],
      aborted: false,
      timeoutCb: null,
      upgradeOrConnect: false,
      parser: null,
      maxHeadersCount: null,
      reusedSocket: false,
      host: 'myhost.mydomain',
      protocol: 'http:',
      _redirectable: [Writable],
      [Symbol(kCapture)]: false,
      [Symbol(kNeedDrain)]: false,
      [Symbol(corked)]: 0,
      [Symbol(kOutHeaders)]: [Object: null prototype]
    },
    data: { code: 'unauthenticated', msg: 'permissions denied' }
  },
  isAxiosError: true,
  toJSON: [Function: toJSON]
}

note: it's also broken on livekit-cli (doesn't throw an error here, but doesn't do anything either)

tested on:
livekit-server-sdk 0.5.6
livekit-server 0.14.0

updateParticipant throws uncatchable 500 error

I have an interval that is supposed to update the metadata for an ingress participant every second like this:

     try {
       roomServiceClient.updateParticipant(room, ingressOptions.participantIdentity, metadata);
     } catch (error) {
       console.error(error);
     }

And it works some of the time, running the full ~200 times (length of the stream), but most of the time it will fail at some point and return an incredibly lengthy 500 error from Axios that is not being caught in the try catch. I don't really care if the request fails occasionally, but I really need it to not crash the script, so any guidance would be appreciated.

Seems perhaps related to this issue: #22

full error
C:\Users\thoma\metaverse\metaverse-backend\node_modules\axios\lib\core\settle.js:19
    reject(new AxiosError(
           ^
AxiosError: Request failed with status code 500
    at settle (C:\Users\thoma\metaverse\metaverse-backend\node_modules\axios\lib\core\settle.js:19:12)
    at IncomingMessage.handleStreamEnd (C:\Users\thoma\metaverse\metaverse-backend\node_modules\axios\lib\adapters\http.js:572:11)
    at IncomingMessage.emit (node:events:526:35)
    at IncomingMessage.emit (node:domain:489:12)
    at endReadableNT (node:internal/streams/readable:1359:12)
    at processTicksAndRejections (node:internal/process/task_queues:82:21) {
  code: 'ERR_BAD_RESPONSE',
  config: {
    transitional: {
      silentJSONParsing: true,
      forcedJSONParsing: true,
      clarifyTimeoutError: false
    },
    adapter: [ 'xhr', 'http' ],
    transformRequest: [ [Function: transformRequest] ],
    transformResponse: [ [Function: transformResponse] ],
    timeout: 0,
    xsrfCookieName: 'XSRF-TOKEN',
    xsrfHeaderName: 'X-XSRF-TOKEN',
    maxContentLength: -1,
    maxBodyLength: -1,
    env: { FormData: [Function], Blob: [class Blob] },
    validateStatus: [Function: validateStatus],
    headers: Object [AxiosHeaders] {
      Accept: 'application/json, text/plain, */*',
      'Content-Type': 'application/json',
      Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2aWRlbyI6eyJyb29tQWRtaW4iOnRydWUsInJvb20iOiIxOmF2OjEifSwiaWF0IjoxNzAyNjAzMjYwLCJuYmYiOjE3MDI2MDMyNjAsImV4cCI6MTcwMjYwMzg2MCwiaXNzIjoiQVBJbUxBTnlBUE5Fa2tVIn0.LjGQsQFTcDmwQwowmf5Fr4674TPtMtxyuDNy8ayNPew',
      'User-Agent': 'axios/1.5.1',
      'Content-Length': '114',
      'Accept-Encoding': 'gzip, compress, deflate, br'
    },
    baseURL: 'https://mkmv.livekit.cloud/',
    method: 'post',
    url: '/twirp/livekit.RoomService/UpdateParticipant',
    data: '{"room":"1:av:1","identity":"streaming_bot","metadata":"{\\"timestamp\\":1702603227817,\\"iteration\\":30}","name":""}'
  },
  request: <ref *1> ClientRequest {
    _events: [Object: null prototype] {
      abort: [Function (anonymous)],
      aborted: [Function (anonymous)],
      connect: [Function (anonymous)],
      error: [Function (anonymous)],
      socket: [Function (anonymous)],
      timeout: [Function (anonymous)],
      finish: [Function: requestOnFinish]
    },
    _eventsCount: 7,
    _maxListeners: undefined,
    outputData: [],
    outputSize: 0,
    writable: true,
    destroyed: false,
    _last: true,
    chunkedEncoding: false,
    shouldKeepAlive: false,
    maxRequestsOnConnectionReached: false,
    _defaultKeepAlive: true,
    useChunkedEncodingByDefault: true,
    sendDate: false,
    _removedConnection: false,
    _removedContLen: false,
    _removedTE: false,
    strictContentLength: false,
    _contentLength: '114',
    _hasBody: true,
    _trailer: '',
    finished: true,
    _headerSent: true,
    _closed: false,
    socket: TLSSocket {
      _tlsOptions: [Object],
      _secureEstablished: true,
      _securePending: false,
      _newSessionPending: false,
      _controlReleased: true,
      secureConnecting: false,
      _SNICallback: null,
      servername: 'mkmv.livekit.cloud',
      alpnProtocol: false,
      authorized: true,
      authorizationError: null,
      encrypted: true,
      _events: [Object: null prototype],
      _eventsCount: 10,
      connecting: false,
      _hadError: false,
      _parent: null,
      _host: 'mkmv.livekit.cloud',
      _closeAfterHandlingError: false,
      _readableState: [ReadableState],
      _maxListeners: undefined,
      _writableState: [WritableState],
      allowHalfOpen: false,
      _sockname: null,
      _pendingData: null,
      _pendingEncoding: '',
      server: undefined,
      _server: null,
      ssl: [TLSWrap],
      _requestCert: true,
      _rejectUnauthorized: true,
      parser: null,
      _httpMessage: [Circular *1],
      [Symbol(res)]: [TLSWrap],
      [Symbol(verified)]: true,
      [Symbol(pendingSession)]: null,
      [Symbol(async_id_symbol)]: 1526,
      [Symbol(kHandle)]: [TLSWrap],
      [Symbol(lastWriteQueueSize)]: 0,
      [Symbol(timeout)]: null,
      [Symbol(kBuffer)]: null,
      [Symbol(kBufferCb)]: null,
      [Symbol(kBufferGen)]: null,
      [Symbol(kCapture)]: false,
      [Symbol(kSetNoDelay)]: false,
      [Symbol(kSetKeepAlive)]: true,
      [Symbol(kSetKeepAliveInitialDelay)]: 60,
      [Symbol(kBytesRead)]: 0,
      [Symbol(kBytesWritten)]: 0,
      [Symbol(connect-options)]: [Object]
    },
    _header: 'POST /twirp/livekit.RoomService/UpdateParticipant HTTP/1.1\r\n' +
      'Accept: application/json, text/plain, */*\r\n' +
      'Content-Type: application/json\r\n' +
      'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2aWRlbyI6eyJyb29tQWRtaW4iOnRydWUsInJvb20iOiIxOmF2OjEifSwiaWF0IjoxNzAyNjAzMjYwLCJuYmYiOjE3MDI2MDMyNjAsImV4cCI6MTcwMjYwMzg2MCwiaXNzIjoiQVBJbUxBTnlBUE5Fa2tVIn0.LjGQsQFTcDmwQwowmf5Fr4674TPtMtxyuDNy8ayNPew\r\n' +
      'User-Agent: axios/1.5.1\r\n' +
      'Content-Length: 114\r\n' +
      'Accept-Encoding: gzip, compress, deflate, br\r\n' +
      'Host: mkmv.livekit.cloud\r\n' +
      'Connection: close\r\n' +
      '\r\n',
    _keepAliveTimeout: 0,
    _onPendingData: [Function: nop],
    agent: Agent {
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      defaultPort: 443,
      protocol: 'https:',
      options: [Object: null prototype],
      requests: [Object: null prototype] {},
      sockets: [Object: null prototype],
      freeSockets: [Object: null prototype] {},
      keepAliveMsecs: 1000,
      keepAlive: false,
      maxSockets: Infinity,
      maxFreeSockets: 256,
      scheduling: 'lifo',
      maxTotalSockets: Infinity,
      totalSocketCount: 2,
      maxCachedSessions: 100,
      _sessionCache: [Object],
      [Symbol(kCapture)]: false
    },
    socketPath: undefined,
    method: 'POST',
    maxHeaderSize: undefined,
    insecureHTTPParser: undefined,
    joinDuplicateHeaders: undefined,
    path: '/twirp/livekit.RoomService/UpdateParticipant',
    _ended: true,
    res: IncomingMessage {
      _readableState: [ReadableState],
      _events: [Object: null prototype],
      _eventsCount: 4,
      _maxListeners: undefined,
      socket: [TLSSocket],
      httpVersionMajor: 1,
      httpVersionMinor: 1,
      httpVersion: '1.1',
      complete: true,
      rawHeaders: [Array],
      rawTrailers: [],
      joinDuplicateHeaders: undefined,
      aborted: false,
      upgrade: false,
      url: '',
      method: null,
      statusCode: 500,
      statusMessage: 'Internal Server Error',
      client: [TLSSocket],
      _consuming: false,
      _dumped: false,
      req: [Circular *1],
      responseUrl: 'https://mkmv.livekit.cloud/twirp/livekit.RoomService/UpdateParticipant',
      redirects: [],
      [Symbol(kCapture)]: false,
      [Symbol(kHeaders)]: [Object],
      [Symbol(kHeadersCount)]: 10,
      [Symbol(kTrailers)]: null,
      [Symbol(kTrailersCount)]: 0
    },
    aborted: false,
    timeoutCb: null,
    upgradeOrConnect: false,
    parser: null,
    maxHeadersCount: null,
    reusedSocket: false,
    host: 'mkmv.livekit.cloud',
    protocol: 'https:',
    _redirectable: Writable {
      _writableState: [WritableState],
      _events: [Object: null prototype],
      _eventsCount: 3,
      _maxListeners: undefined,
      _options: [Object],
      _ended: true,
      _ending: true,
      _redirectCount: 0,
      _redirects: [],
      _requestBodyLength: 114,
      _requestBodyBuffers: [],
      _onNativeResponse: [Function (anonymous)],
      _currentRequest: [Circular *1],
      _currentUrl: 'https://mkmv.livekit.cloud/twirp/livekit.RoomService/UpdateParticipant',
      [Symbol(kCapture)]: false
    },
    [Symbol(kCapture)]: false,
    [Symbol(kBytesWritten)]: 0,
    [Symbol(kNeedDrain)]: false,
    [Symbol(corked)]: 0,
    [Symbol(kOutHeaders)]: [Object: null prototype] {
      accept: [Array],
      'content-type': [Array],
      authorization: [Array],
      'user-agent': [Array],
      'content-length': [Array],
      'accept-encoding': [Array],
      host: [Array]
    },
    [Symbol(errored)]: null,
    [Symbol(kHighWaterMark)]: 16384,
    [Symbol(kRejectNonStandardBodyWrites)]: false,
    [Symbol(kUniqueHeaders)]: null
  },
  response: {
    status: 500,
    statusText: 'Internal Server Error',
    headers: Object [AxiosHeaders] {
      'content-length': '57',
      'content-type': 'application/json',
      date: 'Fri, 15 Dec 2023 01:21:15 GMT',
      vary: 'Origin',
      connection: 'close'
    },
    config: {
      transitional: [Object],
      adapter: [Array],
      transformRequest: [Array],
      transformResponse: [Array],
      timeout: 0,
      xsrfCookieName: 'XSRF-TOKEN',
      xsrfHeaderName: 'X-XSRF-TOKEN',
      maxContentLength: -1,
      maxBodyLength: -1,
      env: [Object],
      validateStatus: [Function: validateStatus],
      headers: [Object [AxiosHeaders]],
      baseURL: 'https://mkmv.livekit.cloud/',
      method: 'post',
      url: '/twirp/livekit.RoomService/UpdateParticipant',
      data: '{"room":"1:av:1","identity":"streaming_bot","metadata":"{\\"timestamp\\":1702603227817,\\"iteration\\":30}","name":""}'
    },
    request: <ref *1> ClientRequest {
      _events: [Object: null prototype],
      _eventsCount: 7,
      _maxListeners: undefined,
      outputData: [],
      outputSize: 0,
      writable: true,
      destroyed: false,
      _last: true,
      chunkedEncoding: false,
      shouldKeepAlive: false,
      maxRequestsOnConnectionReached: false,
      _defaultKeepAlive: true,
      useChunkedEncodingByDefault: true,
      sendDate: false,
      _removedConnection: false,
      _removedContLen: false,
      _removedTE: false,
      strictContentLength: false,
      _contentLength: '114',
      _hasBody: true,
      _trailer: '',
      finished: true,
      _headerSent: true,
      _closed: false,
      socket: [TLSSocket],
      _header: 'POST /twirp/livekit.RoomService/UpdateParticipant HTTP/1.1\r\n' +
        'Accept: application/json, text/plain, */*\r\n' +
        'Content-Type: application/json\r\n' +
        'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2aWRlbyI6eyJyb29tQWRtaW4iOnRydWUsInJvb20iOiIxOmF2OjEifSwiaWF0IjoxNzAyNjAzMjYwLCJuYmYiOjE3MDI2MDMyNjAsImV4cCI6MTcwMjYwMzg2MCwiaXNzIjoiQVBJbUxBTnlBUE5Fa2tVIn0.LjGQsQFTcDmwQwowmf5Fr4674TPtMtxyuDNy8ayNPew\r\n' +
        'User-Agent: axios/1.5.1\r\n' +
        'Content-Length: 114\r\n' +
        'Accept-Encoding: gzip, compress, deflate, br\r\n' +
        'Host: mkmv.livekit.cloud\r\n' +
        'Connection: close\r\n' +
        '\r\n',
      _keepAliveTimeout: 0,
      _onPendingData: [Function: nop],
      agent: [Agent],
      socketPath: undefined,
      method: 'POST',
      maxHeaderSize: undefined,
      insecureHTTPParser: undefined,
      joinDuplicateHeaders: undefined,
      path: '/twirp/livekit.RoomService/UpdateParticipant',
      _ended: true,
      res: [IncomingMessage],
      aborted: false,
      timeoutCb: null,
      upgradeOrConnect: false,
      parser: null,
      maxHeadersCount: null,
      reusedSocket: false,
      host: 'mkmv.livekit.cloud',
      protocol: 'https:',
      _redirectable: [Writable],
      [Symbol(kCapture)]: false,
      [Symbol(kBytesWritten)]: 0,
      [Symbol(kNeedDrain)]: false,
      [Symbol(corked)]: 0,
      [Symbol(kOutHeaders)]: [Object: null prototype],
      [Symbol(errored)]: null,
      [Symbol(kHighWaterMark)]: 16384,
      [Symbol(kRejectNonStandardBodyWrites)]: false,
      [Symbol(kUniqueHeaders)]: null
    },
    data: { code: 'internal', msg: 'operation cannot be completed' }
  }
}

I have some doubts and issues found.....

Issues:

  1. After successful connection between two user, i get the warning in server terminal that " channel congestion detected, updating channel capacity " and then connection gets poor.
  2. When any user start screen sharing the quality suddenly started decreasing.

My doubts:

  1. How can i set expiration of token using node js.
  2. Can you please tell me the server requirement for 10,000 user at a time(both video audio).

Join room using the sdk from server

I'm trying to implement a similar idea that of https://blog.livekit.io/meet-kitt/. I was able to set up a local livekit-server and use a nodeJs app to support creating tokens and rooms. I have also a web hook receiver that is called on room event.
What I want to achieve is to make a bot (text to speech service) respond to the room programmatically but I couldn't find anything from the documentation or code.

Something like KITT ConnectToRoomWithToken or go-server-sdk JoinRoom

How can I achieve the same using javascript sdk??

Setting metadata on all participants causes 500 response

Hi, below is an example to trigger the issue:

import { RoomServiceClient } from "livekit-server-sdk";


const roomName = "Students";

const roomServiceClient = new RoomServiceClient(
  `http://127.0.0.1:7880`,
  "devkey",
  "secret"
);

roomServiceClient.listParticipants(roomName).then((participants) =>
  participants.forEach((participant) => {
    roomServiceClient.updateParticipant(
      roomName,
      participant.identity,
      String.fromCharCode(0 | (Math.random() * 26 + 97))
    );
  })
);
console.log("done");

the server room has multiple participants simulated with livekit-cli load-test and when the above example is run it throws an exception because of 500 code from the twirp server:

Crash ➜ set-metadata node index.js done

/Users/rhx/repos/set-metadata/node_modules/.pnpm/[email protected]/node_modules/axios/dist/node/axios.cjs:1902
reject(new AxiosError(
^
AxiosError: Request failed with status code 500
at settle (/Users/rhx/repos/set-metadata/node_modules/.pnpm/[email protected]/node_modules/axios/dist/node/axios.cjs:1902:12)
at IncomingMessage.handleStreamEnd (/Users/rhx/repos/set-metadata/node_modules/.pnpm/[email protected]/node_modules/axios/dist/node/axios.cjs:2954:11)
at IncomingMessage.emit (node:events:524:35)
at endReadableNT (node:internal/streams/readable:1359:12)
at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {
code: 'ERR_BAD_RESPONSE',
config: {
transitional: {
silentJSONParsing: true,
forcedJSONParsing: true,
clarifyTimeoutError: false
},
adapter: [ 'xhr', 'http' ],
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
env: {
FormData: [Function: FormData] {
LINE_BREAK: '\r\n',
DEFAULT_CONTENT_TYPE: 'application/octet-stream'
},
Blob: [class Blob]
},
validateStatus: [Function: validateStatus],
headers: AxiosHeaders {
Accept: 'application/json, text/plain, /',
'Content-Type': 'application/json',
Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2aWRlbyI6eyJyb29tQWRtaW4iOnRydWUsInJvb20iOiJTdHVkZW50cyJ9LCJpYXQiOjE2ODIzMzQyMzYsIm5iZiI6MTY4MjMzNDIzNiwiZXhwIjoxNjgyMzM0ODM2LCJpc3MiOiJkZXZrZXkifQ.1zsSyWhDALpxctMzutT5xd_ph7GWk9WgMcoDUVcUj40',
'User-Agent': 'axios/1.3.6',
'Content-Length': '65',
'Accept-Encoding': 'gzip, compress, deflate, br'
},
baseURL: 'http://127.0.0.1:7880',
method: 'post',
url: '/twirp/livekit.RoomService/UpdateParticipant',
data: '{"room":"Students","identity":"sdlhl_0","metadata":"c","name":""}'
},
request: <ref *6> ClientRequest {
_events: [Object: null prototype] {
abort: [Function (anonymous)],
aborted: [Function (anonymous)],
connect: [Function (anonymous)],
error: [Function (anonymous)],
socket: [Function (anonymous)],
timeout: [Function (anonymous)],
finish: [Function: requestOnFinish]
},
_eventsCount: 7,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: true,
_last: false,
chunkedEncoding: false,
shouldKeepAlive: true,
maxRequestsOnConnectionReached: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: true,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
strictContentLength: false,
_contentLength: '65',
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
_closed: true,
socket: <ref *1> Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: null,
_closeAfterHandlingError: false,
_readableState: ReadableState {
objectMode: false,
highWaterMark: 16384,
buffer: BufferList { head: null, tail: null, length: 0 },
length: 0,
pipes: [],
flowing: true,
ended: false,
endEmitted: false,
reading: true,
constructed: true,
sync: false,
needReadable: true,
emittedReadable: false,
readableListening: false,
resumeScheduled: false,
errorEmitted: false,
emitClose: false,
autoDestroy: true,
destroyed: false,
errored: null,
closed: false,
closeEmitted: false,
defaultEncoding: 'utf8',
awaitDrainWriters: null,
multiAwaitDrain: false,
readingMore: false,
dataEmitted: true,
decoder: null,
encoding: null,
[Symbol(kPaused)]: false
},
_events: [Object: null prototype] {
end: [Function: onReadableStreamEnd],
free: [Function: onFree],
close: [Function: onClose],
timeout: [Function: onTimeout],
agentRemove: [Function: onRemove],
error: [Function: bound onceWrapper] {
listener: [Function: freeSocketErrorListener]
}
},
_eventsCount: 6,
_maxListeners: undefined,
_writableState: WritableState {
objectMode: false,
highWaterMark: 16384,
finalCalled: false,
needDrain: false,
ending: false,
ended: false,
finished: false,
destroyed: false,
decodeStrings: false,
defaultEncoding: 'utf8',
length: 0,
writing: false,
corked: 0,
sync: false,
bufferProcessing: false,
onwrite: [Function: bound onwrite],
writecb: null,
writelen: 0,
afterWriteTickInfo: null,
buffered: [],
bufferedIndex: 0,
allBuffers: true,
allNoop: true,
pendingcb: 0,
constructed: true,
prefinished: false,
errorEmitted: false,
emitClose: false,
autoDestroy: true,
errored: null,
closed: false,
closeEmitted: false,
[Symbol(kOnFinished)]: []
},
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
timeout: 5000,
parser: null,
_httpMessage: null,
[Symbol(async_id_symbol)]: -1,
[Symbol(kHandle)]: <ref *2> TCP {
reading: true,
onconnection: null,
[Symbol(owner_symbol)]: [Circular *1],
[Symbol(resource_symbol)]: ReusedHandle { type: 34, handle: [Circular *2] }
},
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: <ref *3> Timeout {
_idleTimeout: 5000,
_idlePrev: <ref *4> TimersList {
_idleNext: [Circular *3],
_idlePrev: Timeout {
_idleTimeout: 5000,
_idlePrev: [Circular *3],
_idleNext: [Circular *4],
_idleStart: 657,
_onTimeout: [Function: bound ],
_timerArgs: undefined,
_repeat: null,
_destroyed: false,
[Symbol(refed)]: false,
[Symbol(kHasPrimitive)]: false,
[Symbol(asyncId)]: 47,
[Symbol(triggerId)]: 45
},
expiry: 5127,
id: -9007199254740991,
msecs: 5000,
priorityQueuePosition: 1
},
_idleNext: <ref *5> Timeout {
_idleTimeout: 5000,
_idlePrev: [Circular *3],
_idleNext: <ref *4> TimersList {
_idleNext: [Circular *3],
_idlePrev: [Circular *5],
expiry: 5127,
id: -9007199254740991,
msecs: 5000,
priorityQueuePosition: 1
},
_idleStart: 657,
_onTimeout: [Function: bound ],
_timerArgs: undefined,
_repeat: null,
_destroyed: false,
[Symbol(refed)]: false,
[Symbol(kHasPrimitive)]: false,
[Symbol(asyncId)]: 47,
[Symbol(triggerId)]: 45
},
_idleStart: 2165,
_onTimeout: [Function: bound ],
_timerArgs: undefined,
_repeat: null,
_destroyed: false,
[Symbol(refed)]: false,
[Symbol(kHasPrimitive)]: false,
[Symbol(asyncId)]: 58,
[Symbol(triggerId)]: 56
},
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kSetNoDelay)]: true,
[Symbol(kSetKeepAlive)]: true,
[Symbol(kSetKeepAliveInitialDelay)]: 1,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0
},
_header: 'POST /twirp/livekit.RoomService/UpdateParticipant HTTP/1.1\r\n' +
'Accept: application/json, text/plain, /\r\n' +
'Content-Type: application/json\r\n' +
'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2aWRlbyI6eyJyb29tQWRtaW4iOnRydWUsInJvb20iOiJTdHVkZW50cyJ9LCJpYXQiOjE2ODIzMzQyMzYsIm5iZiI6MTY4MjMzNDIzNiwiZXhwIjoxNjgyMzM0ODM2LCJpc3MiOiJkZXZrZXkifQ.1zsSyWhDALpxctMzutT5xd_ph7GWk9WgMcoDUVcUj40\r\n' +
'User-Agent: axios/1.3.6\r\n' +
'Content-Length: 65\r\n' +
'Accept-Encoding: gzip, compress, deflate, br\r\n' +
'Host: 127.0.0.1:7880\r\n' +
'Connection: keep-alive\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: nop],
agent: Agent {
_events: [Object: null prototype] {
free: [Function (anonymous)],
newListener: [Function: maybeEnableKeylog]
},
_eventsCount: 2,
_maxListeners: undefined,
defaultPort: 80,
protocol: 'http:',
options: [Object: null prototype] {
keepAlive: true,
scheduling: 'lifo',
timeout: 5000,
noDelay: true,
path: null
},
requests: [Object: null prototype] {},
sockets: [Object: null prototype] {},
freeSockets: [Object: null prototype] {
'127.0.0.1:7880:': [
Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: null,
_closeAfterHandlingError: false,
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 6,
_maxListeners: undefined,
_writableState: [WritableState],
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
timeout: 5000,
parser: null,
_httpMessage: null,
[Symbol(async_id_symbol)]: -1,
[Symbol(kHandle)]: [TCP],
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: [Timeout],
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kSetNoDelay)]: true,
[Symbol(kSetKeepAlive)]: true,
[Symbol(kSetKeepAliveInitialDelay)]: 1,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0
},
<ref *1> Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: null,
_closeAfterHandlingError: false,
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 6,
_maxListeners: undefined,
_writableState: [WritableState],
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
timeout: 5000,
parser: null,
_httpMessage: null,
[Symbol(async_id_symbol)]: -1,
[Symbol(kHandle)]: [TCP],
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: [Timeout],
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kSetNoDelay)]: true,
[Symbol(kSetKeepAlive)]: true,
[Symbol(kSetKeepAliveInitialDelay)]: 1,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0
}
]
},
keepAliveMsecs: 1000,
keepAlive: true,
maxSockets: Infinity,
maxFreeSockets: 256,
scheduling: 'lifo',
maxTotalSockets: Infinity,
totalSocketCount: 2,
[Symbol(kCapture)]: false
},
socketPath: undefined,
method: 'POST',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
joinDuplicateHeaders: undefined,
path: '/twirp/livekit.RoomService/UpdateParticipant',
_ended: true,
res: IncomingMessage {
_readableState: ReadableState {
objectMode: false,
highWaterMark: 16384,
buffer: BufferList { head: null, tail: null, length: 0 },
length: 0,
pipes: [],
flowing: true,
ended: true,
endEmitted: true,
reading: false,
constructed: true,
sync: true,
needReadable: false,
emittedReadable: false,
readableListening: false,
resumeScheduled: false,
errorEmitted: false,
emitClose: true,
autoDestroy: true,
destroyed: true,
errored: null,
closed: true,
closeEmitted: true,
defaultEncoding: 'utf8',
awaitDrainWriters: null,
multiAwaitDrain: false,
readingMore: true,
dataEmitted: true,
decoder: null,
encoding: null,
[Symbol(kPaused)]: false
},
_events: [Object: null prototype] {
end: [ [Function: responseOnEnd], [Function: handleStreamEnd] ],
error: [Function: handleStreamError],
data: [Function: handleStreamData],
aborted: [Function: handlerStreamAborted]
},
_eventsCount: 4,
_maxListeners: undefined,
socket: null,
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
rawHeaders: [
'Content-Length',
'57',
'Content-Type',
'application/json',
'Vary',
'Origin',
'Date',
'Mon, 24 Apr 2023 11:03:58 GMT'
],
rawTrailers: [],
joinDuplicateHeaders: undefined,
aborted: false,
upgrade: false,
url: '',
method: null,
statusCode: 500,
statusMessage: 'Internal Server Error',
client: <ref *1> Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: null,
_closeAfterHandlingError: false,
_readableState: ReadableState {
objectMode: false,
highWaterMark: 16384,
buffer: BufferList { head: null, tail: null, length: 0 },
length: 0,
pipes: [],
flowing: true,
ended: false,
endEmitted: false,
reading: true,
constructed: true,
sync: false,
needReadable: true,
emittedReadable: false,
readableListening: false,
resumeScheduled: false,
errorEmitted: false,
emitClose: false,
autoDestroy: true,
destroyed: false,
errored: null,
closed: false,
closeEmitted: false,
defaultEncoding: 'utf8',
awaitDrainWriters: null,
multiAwaitDrain: false,
readingMore: false,
dataEmitted: true,
decoder: null,
encoding: null,
[Symbol(kPaused)]: false
},
_events: [Object: null prototype] {
end: [Function: onReadableStreamEnd],
free: [Function: onFree],
close: [Function: onClose],
timeout: [Function: onTimeout],
agentRemove: [Function: onRemove],
error: [Function: bound onceWrapper] {
listener: [Function: freeSocketErrorListener]
}
},
_eventsCount: 6,
_maxListeners: undefined,
_writableState: WritableState {
objectMode: false,
highWaterMark: 16384,
finalCalled: false,
needDrain: false,
ending: false,
ended: false,
finished: false,
destroyed: false,
decodeStrings: false,
defaultEncoding: 'utf8',
length: 0,
writing: false,
corked: 0,
sync: false,
bufferProcessing: false,
onwrite: [Function: bound onwrite],
writecb: null,
writelen: 0,
afterWriteTickInfo: null,
buffered: [],
bufferedIndex: 0,
allBuffers: true,
allNoop: true,
pendingcb: 0,
constructed: true,
prefinished: false,
errorEmitted: false,
emitClose: false,
autoDestroy: true,
errored: null,
closed: false,
closeEmitted: false,
[Symbol(kOnFinished)]: []
},
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
timeout: 5000,
parser: null,
_httpMessage: null,
[Symbol(async_id_symbol)]: -1,
[Symbol(kHandle)]: <ref *2> TCP {
reading: true,
onconnection: null,
[Symbol(owner_symbol)]: [Circular *1],
[Symbol(resource_symbol)]: ReusedHandle { type: 34, handle: [Circular *2] }
},
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: <ref *3> Timeout {
_idleTimeout: 5000,
_idlePrev: <ref *4> TimersList {
_idleNext: [Circular *3],
_idlePrev: [Timeout],
expiry: 5127,
id: -9007199254740991,
msecs: 5000,
priorityQueuePosition: 1
},
_idleNext: <ref *5> Timeout {
_idleTimeout: 5000,
_idlePrev: [Circular *3],
_idleNext: [TimersList],
_idleStart: 657,
_onTimeout: [Function: bound ],
_timerArgs: undefined,
_repeat: null,
_destroyed: false,
[Symbol(refed)]: false,
[Symbol(kHasPrimitive)]: false,
[Symbol(asyncId)]: 47,
[Symbol(triggerId)]: 45
},
_idleStart: 2165,
_onTimeout: [Function: bound ],
_timerArgs: undefined,
_repeat: null,
_destroyed: false,
[Symbol(refed)]: false,
[Symbol(kHasPrimitive)]: false,
[Symbol(asyncId)]: 58,
[Symbol(triggerId)]: 56
},
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kSetNoDelay)]: true,
[Symbol(kSetKeepAlive)]: true,
[Symbol(kSetKeepAliveInitialDelay)]: 1,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0
},
_consuming: false,
_dumped: false,
req: [Circular *6],
responseUrl: 'http://127.0.0.1:7880/twirp/livekit.RoomService/UpdateParticipant',
redirects: [],
[Symbol(kCapture)]: false,
[Symbol(kHeaders)]: {
'content-length': '57',
'content-type': 'application/json',
vary: 'Origin',
date: 'Mon, 24 Apr 2023 11:03:58 GMT'
},
[Symbol(kHeadersCount)]: 8,
[Symbol(kTrailers)]: null,
[Symbol(kTrailersCount)]: 0
},
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: true,
host: '127.0.0.1',
protocol: 'http:',
_redirectable: Writable {
_writableState: WritableState {
objectMode: false,
highWaterMark: 16384,
finalCalled: false,
needDrain: false,
ending: false,
ended: false,
finished: false,
destroyed: false,
decodeStrings: true,
defaultEncoding: 'utf8',
length: 0,
writing: false,
corked: 0,
sync: true,
bufferProcessing: false,
onwrite: [Function: bound onwrite],
writecb: null,
writelen: 0,
afterWriteTickInfo: null,
buffered: [],
bufferedIndex: 0,
allBuffers: true,
allNoop: true,
pendingcb: 0,
constructed: true,
prefinished: false,
errorEmitted: false,
emitClose: true,
autoDestroy: true,
errored: null,
closed: false,
closeEmitted: false,
[Symbol(kOnFinished)]: []
},
_events: [Object: null prototype] {
response: [Function: handleResponse],
error: [Function: handleRequestError],
socket: [Function: handleRequestSocket]
},
_eventsCount: 3,
_maxListeners: undefined,
_options: {
maxRedirects: 21,
maxBodyLength: Infinity,
protocol: 'http:',
path: '/twirp/livekit.RoomService/UpdateParticipant',
method: 'POST',
headers: [Object: null prototype] {
Accept: 'application/json, text/plain, /',
'Content-Type': 'application/json',
Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2aWRlbyI6eyJyb29tQWRtaW4iOnRydWUsInJvb20iOiJTdHVkZW50cyJ9LCJpYXQiOjE2ODIzMzQyMzYsIm5iZiI6MTY4MjMzNDIzNiwiZXhwIjoxNjgyMzM0ODM2LCJpc3MiOiJkZXZrZXkifQ.1zsSyWhDALpxctMzutT5xd_ph7GWk9WgMcoDUVcUj40',
'User-Agent': 'axios/1.3.6',
'Content-Length': '65',
'Accept-Encoding': 'gzip, compress, deflate, br'
},
agents: { http: undefined, https: undefined },
auth: undefined,
beforeRedirect: [Function: dispatchBeforeRedirect],
beforeRedirects: { proxy: [Function: beforeRedirect] },
hostname: '127.0.0.1',
port: '7880',
agent: undefined,
nativeProtocols: {
'http:': {
_connectionListener: [Function: connectionListener],
METHODS: [Array],
STATUS_CODES: [Object],
Agent: [Function],
ClientRequest: [Function: ClientRequest],
IncomingMessage: [Function: IncomingMessage],
OutgoingMessage: [Function: OutgoingMessage],
Server: [Function: Server],
ServerResponse: [Function: ServerResponse],
createServer: [Function: createServer],
validateHeaderName: [Function: _node_internal],
validateHeaderValue: [Function: _node_internal],
get: [Function: get],
request: [Function: request],
setMaxIdleHTTPParsers: [Function: setMaxIdleHTTPParsers],
maxHeaderSize: [Getter],
globalAgent: [Getter/Setter]
},
'https:': {
Agent: [Function: Agent],
globalAgent: [Agent],
Server: [Function: Server],
createServer: [Function: createServer],
get: [Function: get],
request: [Function: request]
}
},
pathname: '/twirp/livekit.RoomService/UpdateParticipant'
},
_ended: true,
_ending: true,
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 65,
_requestBodyBuffers: [],
_onNativeResponse: [Function (anonymous)],
_currentRequest: [Circular *6],
_currentUrl: 'http://127.0.0.1:7880/twirp/livekit.RoomService/UpdateParticipant',
[Symbol(kCapture)]: false
},
[Symbol(kCapture)]: false,
[Symbol(kBytesWritten)]: 0,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype] {
accept: [ 'Accept', 'application/json, text/plain, /' ],
'content-type': [ 'Content-Type', 'application/json' ],
authorization: [
'Authorization',
'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2aWRlbyI6eyJyb29tQWRtaW4iOnRydWUsInJvb20iOiJTdHVkZW50cyJ9LCJpYXQiOjE2ODIzMzQyMzYsIm5iZiI6MTY4MjMzNDIzNiwiZXhwIjoxNjgyMzM0ODM2LCJpc3MiOiJkZXZrZXkifQ.1zsSyWhDALpxctMzutT5xd_ph7GWk9WgMcoDUVcUj40'
],
'user-agent': [ 'User-Agent', 'axios/1.3.6' ],
'content-length': [ 'Content-Length', '65' ],
'accept-encoding': [ 'Accept-Encoding', 'gzip, compress, deflate, br' ],
host: [ 'Host', '127.0.0.1:7880' ]
},
[Symbol(errored)]: null,
[Symbol(kUniqueHeaders)]: null
},
response: {
status: 500,
statusText: 'Internal Server Error',
headers: AxiosHeaders {
'content-length': '57',
'content-type': 'application/json',
vary: 'Origin',
date: 'Mon, 24 Apr 2023 11:03:58 GMT'
},
config: {
transitional: {
silentJSONParsing: true,
forcedJSONParsing: true,
clarifyTimeoutError: false
},
adapter: [ 'xhr', 'http' ],
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
env: {
FormData: [Function: FormData] {
LINE_BREAK: '\r\n',
DEFAULT_CONTENT_TYPE: 'application/octet-stream'
},
Blob: [class Blob]
},
validateStatus: [Function: validateStatus],
headers: AxiosHeaders {
Accept: 'application/json, text/plain, /',
'Content-Type': 'application/json',
Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2aWRlbyI6eyJyb29tQWRtaW4iOnRydWUsInJvb20iOiJTdHVkZW50cyJ9LCJpYXQiOjE2ODIzMzQyMzYsIm5iZiI6MTY4MjMzNDIzNiwiZXhwIjoxNjgyMzM0ODM2LCJpc3MiOiJkZXZrZXkifQ.1zsSyWhDALpxctMzutT5xd_ph7GWk9WgMcoDUVcUj40',
'User-Agent': 'axios/1.3.6',
'Content-Length': '65',
'Accept-Encoding': 'gzip, compress, deflate, br'
},
baseURL: 'http://127.0.0.1:7880',
method: 'post',
url: '/twirp/livekit.RoomService/UpdateParticipant',
data: '{"room":"Students","identity":"sdlhl_0","metadata":"c","name":""}'
},
request: <ref *6> ClientRequest {
_events: [Object: null prototype] {
abort: [Function (anonymous)],
aborted: [Function (anonymous)],
connect: [Function (anonymous)],
error: [Function (anonymous)],
socket: [Function (anonymous)],
timeout: [Function (anonymous)],
finish: [Function: requestOnFinish]
},
_eventsCount: 7,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: true,
_last: false,
chunkedEncoding: false,
shouldKeepAlive: true,
maxRequestsOnConnectionReached: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: true,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
strictContentLength: false,
_contentLength: '65',
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
_closed: true,
socket: <ref *1> Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: null,
_closeAfterHandlingError: false,
_readableState: ReadableState {
objectMode: false,
highWaterMark: 16384,
buffer: BufferList { head: null, tail: null, length: 0 },
length: 0,
pipes: [],
flowing: true,
ended: false,
endEmitted: false,
reading: true,
constructed: true,
sync: false,
needReadable: true,
emittedReadable: false,
readableListening: false,
resumeScheduled: false,
errorEmitted: false,
emitClose: false,
autoDestroy: true,
destroyed: false,
errored: null,
closed: false,
closeEmitted: false,
defaultEncoding: 'utf8',
awaitDrainWriters: null,
multiAwaitDrain: false,
readingMore: false,
dataEmitted: true,
decoder: null,
encoding: null,
[Symbol(kPaused)]: false
},
_events: [Object: null prototype] {
end: [Function: onReadableStreamEnd],
free: [Function: onFree],
close: [Function: onClose],
timeout: [Function: onTimeout],
agentRemove: [Function: onRemove],
error: [Function: bound onceWrapper] {
listener: [Function: freeSocketErrorListener]
}
},
_eventsCount: 6,
_maxListeners: undefined,
_writableState: WritableState {
objectMode: false,
highWaterMark: 16384,
finalCalled: false,
needDrain: false,
ending: false,
ended: false,
finished: false,
destroyed: false,
decodeStrings: false,
defaultEncoding: 'utf8',
length: 0,
writing: false,
corked: 0,
sync: false,
bufferProcessing: false,
onwrite: [Function: bound onwrite],
writecb: null,
writelen: 0,
afterWriteTickInfo: null,
buffered: [],
bufferedIndex: 0,
allBuffers: true,
allNoop: true,
pendingcb: 0,
constructed: true,
prefinished: false,
errorEmitted: false,
emitClose: false,
autoDestroy: true,
errored: null,
closed: false,
closeEmitted: false,
[Symbol(kOnFinished)]: []
},
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
timeout: 5000,
parser: null,
_httpMessage: null,
[Symbol(async_id_symbol)]: -1,
[Symbol(kHandle)]: <ref *2> TCP {
reading: true,
onconnection: null,
[Symbol(owner_symbol)]: [Circular *1],
[Symbol(resource_symbol)]: ReusedHandle { type: 34, handle: [Circular *2] }
},
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: <ref *3> Timeout {
_idleTimeout: 5000,
_idlePrev: <ref *4> TimersList {
_idleNext: [Circular *3],
_idlePrev: [Timeout],
expiry: 5127,
id: -9007199254740991,
msecs: 5000,
priorityQueuePosition: 1
},
_idleNext: <ref *5> Timeout {
_idleTimeout: 5000,
_idlePrev: [Circular *3],
_idleNext: [TimersList],
_idleStart: 657,
_onTimeout: [Function: bound ],
_timerArgs: undefined,
_repeat: null,
_destroyed: false,
[Symbol(refed)]: false,
[Symbol(kHasPrimitive)]: false,
[Symbol(asyncId)]: 47,
[Symbol(triggerId)]: 45
},
_idleStart: 2165,
_onTimeout: [Function: bound ],
_timerArgs: undefined,
_repeat: null,
_destroyed: false,
[Symbol(refed)]: false,
[Symbol(kHasPrimitive)]: false,
[Symbol(asyncId)]: 58,
[Symbol(triggerId)]: 56
},
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kSetNoDelay)]: true,
[Symbol(kSetKeepAlive)]: true,
[Symbol(kSetKeepAliveInitialDelay)]: 1,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0
},
_header: 'POST /twirp/livekit.RoomService/UpdateParticipant HTTP/1.1\r\n' +
'Accept: application/json, text/plain, /\r\n' +
'Content-Type: application/json\r\n' +
'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2aWRlbyI6eyJyb29tQWRtaW4iOnRydWUsInJvb20iOiJTdHVkZW50cyJ9LCJpYXQiOjE2ODIzMzQyMzYsIm5iZiI6MTY4MjMzNDIzNiwiZXhwIjoxNjgyMzM0ODM2LCJpc3MiOiJkZXZrZXkifQ.1zsSyWhDALpxctMzutT5xd_ph7GWk9WgMcoDUVcUj40\r\n' +
'User-Agent: axios/1.3.6\r\n' +
'Content-Length: 65\r\n' +
'Accept-Encoding: gzip, compress, deflate, br\r\n' +
'Host: 127.0.0.1:7880\r\n' +
'Connection: keep-alive\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: nop],
agent: Agent {
_events: [Object: null prototype] {
free: [Function (anonymous)],
newListener: [Function: maybeEnableKeylog]
},
_eventsCount: 2,
_maxListeners: undefined,
defaultPort: 80,
protocol: 'http:',
options: [Object: null prototype] {
keepAlive: true,
scheduling: 'lifo',
timeout: 5000,
noDelay: true,
path: null
},
requests: [Object: null prototype] {},
sockets: [Object: null prototype] {},
freeSockets: [Object: null prototype] {
'127.0.0.1:7880:': [ [Socket], [Socket] ]
},
keepAliveMsecs: 1000,
keepAlive: true,
maxSockets: Infinity,
maxFreeSockets: 256,
scheduling: 'lifo',
maxTotalSockets: Infinity,
totalSocketCount: 2,
[Symbol(kCapture)]: false
},
socketPath: undefined,
method: 'POST',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
joinDuplicateHeaders: undefined,
path: '/twirp/livekit.RoomService/UpdateParticipant',
_ended: true,
res: IncomingMessage {
_readableState: ReadableState {
objectMode: false,
highWaterMark: 16384,
buffer: BufferList { head: null, tail: null, length: 0 },
length: 0,
pipes: [],
flowing: true,
ended: true,
endEmitted: true,
reading: false,
constructed: true,
sync: true,
needReadable: false,
emittedReadable: false,
readableListening: false,
resumeScheduled: false,
errorEmitted: false,
emitClose: true,
autoDestroy: true,
destroyed: true,
errored: null,
closed: true,
closeEmitted: true,
defaultEncoding: 'utf8',
awaitDrainWriters: null,
multiAwaitDrain: false,
readingMore: true,
dataEmitted: true,
decoder: null,
encoding: null,
[Symbol(kPaused)]: false
},
_events: [Object: null prototype] {
end: [ [Function: responseOnEnd], [Function: handleStreamEnd] ],
error: [Function: handleStreamError],
data: [Function: handleStreamData],
aborted: [Function: handlerStreamAborted]
},
_eventsCount: 4,
_maxListeners: undefined,
socket: null,
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
rawHeaders: [
'Content-Length',
'57',
'Content-Type',
'application/json',
'Vary',
'Origin',
'Date',
'Mon, 24 Apr 2023 11:03:58 GMT'
],
rawTrailers: [],
joinDuplicateHeaders: undefined,
aborted: false,
upgrade: false,
url: '',
method: null,
statusCode: 500,
statusMessage: 'Internal Server Error',
client: <ref *1> Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: null,
_closeAfterHandlingError: false,
_readableState: ReadableState {
objectMode: false,
highWaterMark: 16384,
buffer: [BufferList],
length: 0,
pipes: [],
flowing: true,
ended: false,
endEmitted: false,
reading: true,
constructed: true,
sync: false,
needReadable: true,
emittedReadable: false,
readableListening: false,
resumeScheduled: false,
errorEmitted: false,
emitClose: false,
autoDestroy: true,
destroyed: false,
errored: null,
closed: false,
closeEmitted: false,
defaultEncoding: 'utf8',
awaitDrainWriters: null,
multiAwaitDrain: false,
readingMore: false,
dataEmitted: true,
decoder: null,
encoding: null,
[Symbol(kPaused)]: false
},
_events: [Object: null prototype] {
end: [Function: onReadableStreamEnd],
free: [Function: onFree],
close: [Function: onClose],
timeout: [Function: onTimeout],
agentRemove: [Function: onRemove],
error: [Function]
},
_eventsCount: 6,
_maxListeners: undefined,
_writableState: WritableState {
objectMode: false,
highWaterMark: 16384,
finalCalled: false,
needDrain: false,
ending: false,
ended: false,
finished: false,
destroyed: false,
decodeStrings: false,
defaultEncoding: 'utf8',
length: 0,
writing: false,
corked: 0,
sync: false,
bufferProcessing: false,
onwrite: [Function: bound onwrite],
writecb: null,
writelen: 0,
afterWriteTickInfo: null,
buffered: [],
bufferedIndex: 0,
allBuffers: true,
allNoop: true,
pendingcb: 0,
constructed: true,
prefinished: false,
errorEmitted: false,
emitClose: false,
autoDestroy: true,
errored: null,
closed: false,
closeEmitted: false,
[Symbol(kOnFinished)]: []
},
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
timeout: 5000,
parser: null,
_httpMessage: null,
[Symbol(async_id_symbol)]: -1,
[Symbol(kHandle)]: <ref *2> TCP {
reading: true,
onconnection: null,
[Symbol(owner_symbol)]: [Circular *1],
[Symbol(resource_symbol)]: [ReusedHandle]
},
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: <ref *3> Timeout {
_idleTimeout: 5000,
_idlePrev: [TimersList],
_idleNext: [Timeout],
_idleStart: 2165,
_onTimeout: [Function: bound ],
_timerArgs: undefined,
_repeat: null,
_destroyed: false,
[Symbol(refed)]: false,
[Symbol(kHasPrimitive)]: false,
[Symbol(asyncId)]: 58,
[Symbol(triggerId)]: 56
},
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kSetNoDelay)]: true,
[Symbol(kSetKeepAlive)]: true,
[Symbol(kSetKeepAliveInitialDelay)]: 1,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0
},
_consuming: false,
_dumped: false,
req: [Circular *6],
responseUrl: 'http://127.0.0.1:7880/twirp/livekit.RoomService/UpdateParticipant',
redirects: [],
[Symbol(kCapture)]: false,
[Symbol(kHeaders)]: {
'content-length': '57',
'content-type': 'application/json',
vary: 'Origin',
date: 'Mon, 24 Apr 2023 11:03:58 GMT'
},
[Symbol(kHeadersCount)]: 8,
[Symbol(kTrailers)]: null,
[Symbol(kTrailersCount)]: 0
},
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: true,
host: '127.0.0.1',
protocol: 'http:',
_redirectable: Writable {
_writableState: WritableState {
objectMode: false,
highWaterMark: 16384,
finalCalled: false,
needDrain: false,
ending: false,
ended: false,
finished: false,
destroyed: false,
decodeStrings: true,
defaultEncoding: 'utf8',
length: 0,
writing: false,
corked: 0,
sync: true,
bufferProcessing: false,
onwrite: [Function: bound onwrite],
writecb: null,
writelen: 0,
afterWriteTickInfo: null,
buffered: [],
bufferedIndex: 0,
allBuffers: true,
allNoop: true,
pendingcb: 0,
constructed: true,
prefinished: false,
errorEmitted: false,
emitClose: true,
autoDestroy: true,
errored: null,
closed: false,
closeEmitted: false,
[Symbol(kOnFinished)]: []
},
_events: [Object: null prototype] {
response: [Function: handleResponse],
error: [Function: handleRequestError],
socket: [Function: handleRequestSocket]
},
_eventsCount: 3,
_maxListeners: undefined,
_options: {
maxRedirects: 21,
maxBodyLength: Infinity,
protocol: 'http:',
path: '/twirp/livekit.RoomService/UpdateParticipant',
method: 'POST',
headers: [Object: null prototype] {
Accept: 'application/json, text/plain, /',
'Content-Type': 'application/json',
Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2aWRlbyI6eyJyb29tQWRtaW4iOnRydWUsInJvb20iOiJTdHVkZW50cyJ9LCJpYXQiOjE2ODIzMzQyMzYsIm5iZiI6MTY4MjMzNDIzNiwiZXhwIjoxNjgyMzM0ODM2LCJpc3MiOiJkZXZrZXkifQ.1zsSyWhDALpxctMzutT5xd_ph7GWk9WgMcoDUVcUj40',
'User-Agent': 'axios/1.3.6',
'Content-Length': '65',
'Accept-Encoding': 'gzip, compress, deflate, br'
},
agents: { http: undefined, https: undefined },
auth: undefined,
beforeRedirect: [Function: dispatchBeforeRedirect],
beforeRedirects: { proxy: [Function: beforeRedirect] },
hostname: '127.0.0.1',
port: '7880',
agent: undefined,
nativeProtocols: { 'http:': [Object], 'https:': [Object] },
pathname: '/twirp/livekit.RoomService/UpdateParticipant'
},
_ended: true,
_ending: true,
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 65,
_requestBodyBuffers: [],
_onNativeResponse: [Function (anonymous)],
_currentRequest: [Circular *6],
_currentUrl: 'http://127.0.0.1:7880/twirp/livekit.RoomService/UpdateParticipant',
[Symbol(kCapture)]: false
},
[Symbol(kCapture)]: false,
[Symbol(kBytesWritten)]: 0,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype] {
accept: [ 'Accept', 'application/json, text/plain, /' ],
'content-type': [ 'Content-Type', 'application/json' ],
authorization: [
'Authorization',
'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2aWRlbyI6eyJyb29tQWRtaW4iOnRydWUsInJvb20iOiJTdHVkZW50cyJ9LCJpYXQiOjE2ODIzMzQyMzYsIm5iZiI6MTY4MjMzNDIzNiwiZXhwIjoxNjgyMzM0ODM2LCJpc3MiOiJkZXZrZXkifQ.1zsSyWhDALpxctMzutT5xd_ph7GWk9WgMcoDUVcUj40'
],
'user-agent': [ 'User-Agent', 'axios/1.3.6' ],
'content-length': [ 'Content-Length', '65' ],
'accept-encoding': [ 'Accept-Encoding', 'gzip, compress, deflate, br' ],
host: [ 'Host', '127.0.0.1:7880' ]
},
[Symbol(errored)]: null,
[Symbol(kUniqueHeaders)]: null
},
data: { code: 'internal', msg: 'operation cannot be completed' }
}
}

Node.js v19.8.1
➜ set-metadata

sendData is not working

First problem. I am using:

await this.client.sendData(roomId, encoder.encode(JSON.stringify(message)), DataPacket_Kind.RELIABLE, {});

The data is sent successfully, but none of the recipients receive the data. The client settings are all fine, and I can create and use rooms without any issues.
Second problem: After several minutes of creating a room, this method starts throwing an error

Error: Request failed with status 503: Service Unavailable
    at <anonymous> (./app/backend/node_modules/livekit-server-sdk/dist/TwirpRPC.js:27:14)
    at processTicksAndRejections (:12:39)
      at ./app/backend/src/lib/LiveKitManager.ts:73:13

and I don't know why. After creating a new room, the method works for 5-7 minutes before encountering the same error again (but clients do not receive anything in both scenarios).

AccessToken.addGrant() should not replace previous grants

Consider this following use case:

const token = new AccessToken();

token.addGrant({
 canPublish: true,
 canSubscribe: true,
 roomJoin: true,
 room: data.meetingID,
 canPublishData: true,
});

if( user.isAdmin ){
 token.addGrant({  roomAdmin: true })
}

This code should keep the previous grants and add roomAdmin grant to the token, instead, it replaces previous grants and adds only roomAdmin grant to the token.

I am not sure if this is intentional, I was not able to join a room with just roomAdmin grant.

I can open a PR that fixes this issue.

this.grants.video = grant;

Documentation Update: `emptyTimeout` in `CreateOptions`

Current Documentation

/**
 * number of seconds the room should clean up after being empty
 */
emptyTimeout?: number;

Proposed Documentation

  /**
   * The `emptyTimeout` property represents the number of seconds a room should remain open after being empty.
   *
   * Behavior:
   * 1. When creating a room with an `emptyTimeout` of, for example, 10 minutes:
   *    - If no participant joins within 20 seconds of room creation, the room will automatically close.
   *
   * 2. If a participant joins and later disconnects:
   *    - The empty timeout countdown begins only after the last participant disconnects.
   *    - If the last participant disconnects and no new participant joins, the room will continue to exist for the
   *      specified `emptyTimeout` duration (e.g., 10 minutes) before automatically closing.
   *    - If a new participant joins before the empty timeout elapses, the countdown is reset, and the room remains open.
   */
  emptyTimeout?: number;

livekit server example

I made a lot of searches and I did not find any example or tutorial for js server, please provide it

key "name" missing in WebhookEvent type

the WeebhookEvent for ingress_created or ingress_ended can have a name key under ingressInfo which the same name for creating the ingress using CreateIngressOptions.

Screenshot 2024-02-04 at 12 13 20 AM Screenshot 2024-02-04 at 12 13 47 AM

mutePublishedTrack doesn't unmute tracks

when you mute a track, it works fine:
RoomServiceClient.mutePublishedTrack(room, identity, true)

but it doesn't do anything if you try to unmute it:
RoomServiceClient.mutePublishedTrack(room, identity, false)

the TrackInfo returned by that last call incorrectly reports a muted=false, but if you query the participant trackinfo it will still report muted=true

tested on:
livekit-server-sdk 0.5.6
livekit-server 0.14.0

Subscribe/Unsubscribe to many users at the same time

Hi, y'all!

I'm new in the LiveKit environment and I have a question. Is there a way I can update the subscriptions for many users instead of one by one? Because I ran into this:

I have a set of users (User 1, User 2, User 3, and User 4), so the idea is that User 1 can talk just to 2 of them or all of them, but I see that the only way to update the stream channel is by using the function updateSubscriptions individually.

const changeUserSub = async (req, res) => {
    const { room } = req.params;
    const { users, sids, subscribe } = req.body;

    const usersPromises = users.map((user) => {
        return svc.updateSubscriptions(room, user, sids, subscribe);
    });

    try {
        await Promise.all(usersPromises);
    catch(_) {
        ...
    }
    ...
}

I have to loop in all the users and change the subscriptions one by one for doing something like this:

    {
      "users": ["User 2", "User 3"],
      "sids": ["TR_AMfKU8MF3wGQjn"], // This is User 1 track id
      "subscribe": true
    }

Thanks!
Really appreciate it if someone can help me :)

[BUG] listEgress throws an error when trying to parse what is already JSON data

Summary

The method egressClient.listEgress(roomName?) is throwing SyntaxError: Unexpected token o in JSON at position 1 because JSON.parse is invoked on a data structure that is already in a JSON format as seen on the following screenshot taken for a debugger attached to the nodeJS process:

image


The error lies at the line of EgressClient in the function listEgress: https://github.com/livekit/server-sdk-js/blob/main/src/EgressClient.ts#L244

Using NodeJS v16.13.0 and livekit-server-sdk v1.0.0, data at line 238 is already in JSON format.
This causes the following error to be thrown:

SyntaxError: Unexpected token o in JSON at position 1
    at JSON.parse (<anonymous>)
    at EgressClient.<anonymous> (/api/node_modules/livekit-server-sdk/dist/EgressClient.js:184:31)
    at Generator.next (<anonymous>)
    at fulfilled (/api/node_modules/livekit-server-sdk/dist/EgressClient.js:5:58)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)

Issue Type

Bug Report

Component Name

EgressClient

OS / Environment

  • Docker version 20.10.17, build 100c701
  • NodeJS docker container v16.13.0
  • livekit-server-sdk v1.0.0 (nodeJS dependency)
  • Egress docker container v1.0
  • Livekit docker container v1.0

Steps to reproduce

Just run the following snippet.

  const egressClient = new EgressClient(LIVEKIT_URL, LIVEKIT_API_KEY, LIVEKIT_API_SECRET);
  const egressListForRoom = await egressClient.listEgress(); // throws "SyntaxError: Unexpected token o in JSON at position 1"

Dependency issue upgrading to 1.2.0

Got this error today:

...\node_modules\livekit-server-sdk\dist\TwirpRPC.js:8
const camelcase_keys_1 = __importDefault(require("camelcase-keys"));
                                         ^
Error [ERR_REQUIRE_ESM]: require() of ES Module ...\node_modules\camelcase-keys\index.js from ...\node_modules\livekit-server-sdk\dist\TwirpRPC.js not supported.
Instead change the require of index.js in ...\node_modules\livekit-server-sdk\dist\TwirpRPC.js to a dynamic import() which is available in all CommonJS modules.
    at Object.<anonymous> (...\node_modules\livekit-server-sdk\dist\TwirpRPC.js:8:42)
    at Object.<anonymous> (...\node_modules\livekit-server-sdk\dist\EgressClient.js:18:20)
    at Object.<anonymous> (...\node_modules\livekit-server-sdk\dist\index.js:15:14)
    at Object.<anonymous> (...\dist\src\livekit\livekit.service.js:15:30)
    at Object.<anonymous> (...\dist\src\app.module.js:43:27)
    at Object.<anonymous> (...\dist\src\main.js:8:22)

Fixed by downgrading to 1.1.4

Hidden participant Can Not subscribe to published data

Hello Livekit team!

In my use case I need to let a hidden participant to join the room with permission to subscribe ONLY for both Audio/Video tracks and Published data!
I added the following token

at.addGrant({
            roomJoin: true,
            room: "example",
            canPublish: false,           
            canPublishData: false,
            roomAdmin: false,
           
            canSubscribe: true,
            hidden: true,
          });

This participant can subscribe to published Audio/Video But not to published data

Update
After upgrading livekit-server-sdk to v1.0.2 and restarting the server, It works now

Please close this issue

Cloudflare Worker support - avoid using 'crypto' from Node

Just used wrangler locally to try the Cloudflare Worker support. I know a lot has been done recently to add support for non-Node JS environments.

Tried it at this commit from yesterday:
1c2e183

Here's the last thing I'm running into:

    src/util/server-sdk-js-main/dist/WebhookReceiver.js:1:19:
      1 │ import crypto from 'crypto';                       
        ╵                    ~~~~~~~~

  The package "crypto" wasn't found on the file system but is built into node.

They recommend the Web Crypto API: https://developers.cloudflare.com/workers/runtime-apis/web-crypto

After just commenting that code out locally, it was able to generate a JWT while running in wrangler - so I think this is the last thing left. Thanks!

Safe-buffer error when using livekit-server-sdk

Describe the bug
Getting an error related to safe-buffer when importing livekit-server-sdk. I've checked my other code and it looks like this is the only thing that's causing the error (it works just fine when I don't import it).

Server

  • Version: 1.0.0
  • Environment: local, Node
  • Using ViteJS
  • Using local Docker container as server

Client

  • SDK: React
  • Version: 1.0.1
  • Browsers: Chromium, Firefox

To Reproduce
Steps to reproduce the behavior:

  1. Copy code below
  2. Run project and check dev console (wish I could say more, but not sure what's causing this)

Expected behavior
livekit-server-sdk should import without giving an error.

Screenshots
image

Additional context
I'm very new to LiveKit, so I'm using the online playground as a starting point [https://livekit.io/playground#code=045cdb4c65c1876645e0c00b]. A lot of the code is taken from there.

Code
`
import { render } from "react-dom";
import React from "react";
import "../index.css";
import * as ReactFeather from "react-feather";
import * as LiveKitReact from "@livekit/react-components";
import * as Chakra from "@chakra-ui/react";
import "livekit-server-sdk";

/*
Playground lets you build a real-time video room using LiveKit's React component.
Feel free to cutomize the code below to your heart's content.
Send this page to a friend or open it in a new browser tab to see multi-user conferencing in action.

In scope:
token: An access token that joins the room, valid for 2h.
React: The React library object
LiveKitReact: The LiveKit React client SDK
LiveKitClient: The LiveKit JavaScript client SDK
ReactFeather: Feather icons to make things snazzy
Chakra: ChakraUI for React
*/

const { useParticipant, VideoRenderer, AudioRenderer, LiveKitRoom } =
LiveKitReact;

const { Flex, Grid, HStack, VStack, Box, Text, Icon, Button } = Chakra;

const { Mic, MicOff, Video, VideoOff, Monitor, XSquare, Power } = ReactFeather;

const RoomView = () => (

<LiveKitRoom
url={url}
token={token}
stageRenderer={StageView}
onConnected={(room) => {
handleConnected(room);
}}
/>

);

const CustomParticipantView = ({ participant }) => {
const { cameraPublication, isLocal } = useParticipant(participant);
if (
!cameraPublication ||
!cameraPublication.isSubscribed ||
!cameraPublication.track ||
cameraPublication.isMuted
) {
return null;
}
return (



);
};

const ControlButton = ({ icon, onClick, children }) => (




{children}



);

const ControlsView = ({ room }) => {
const { unpublishTrack } = useParticipant(room.localParticipant);

const onToggleMic = () => {
const enabled = room.localParticipant.isMicrophoneEnabled;
room.localParticipant.setMicrophoneEnabled(!enabled);
};
const onToggleVideo = () => {
const enabled = room.localParticipant.isCameraEnabled;
room.localParticipant.setCameraEnabled(!enabled);
};
const onPowerOff = () => {
room.disconnect();
};

return (

<ControlButton
icon={room.localParticipant.isMicrophoneEnabled ? Mic : MicOff}
onClick={onToggleMic}
>
{room.localParticipant.isMicrophoneEnabled ? "Mute" : "Unmute"}

<ControlButton
icon={room.localParticipant.isCameraEnabled ? Video : VideoOff}
onClick={onToggleVideo}
>
{room.localParticipant.isCameraEnabled ? "Stop Video" : "Start Video"}


Disconnect


);
};

const RoomStatusView = ({ children }) => (


{children}


);

// renderStage prepares the layout of the stage using subcomponents. Feel free to
// modify as you see fit. It uses the built-in ParticipantView component in this
// example; you may use a custom component instead.
function StageView({ roomState }) {
const { room, participants, audioTracks, isConnecting, error } = roomState;
const gridRef = React.useRef(null);
const [gridTemplateColumns, setGridTemplateColumns] = React.useState("1fr");

React.useEffect(() => {
const gridEl = gridRef.current;
if (!gridEl || participants.length === 0) return;

const totalWidth = gridEl.clientWidth;
const numCols = Math.ceil(Math.sqrt(participants.length));
const colSize = Math.floor(totalWidth / numCols);
setGridTemplateColumns(`repeat(${numCols}, minmax(50px, ${colSize}px))`);

}, [participants]);

if (isConnecting) {
return Connecting...;
}
if (error) {
return Error: {error.message};
}
if (!room) {
return Room closed;
}

return (

<Grid
ref={gridRef}
__css={{
display: "grid",
aspectRatio: "1.77778",
overflow: "hidden",
background: "black",
alignItems: "center",
justifyContent: "center",
width: "100%",
gridTemplateColumns: gridTemplateColumns,
}}
>
{audioTracks.map((track) => (

))}
{participants.map((participant) => (

))}



);
}

async function handleConnected(room) {
console.log("connected to room", room);

const tracks = await LiveKitClient.createLocalTracks({
audio: true,
video: true,
});
tracks.forEach((track) => {
room.localParticipant.publishTrack(track, { simulcast: true });
});
}

export default RoomView;
'

StartRecording throws an uncatchable axios error

I'm running livekit-server-sdk 0.5.9 and livekit-server v0.15.5 in service mode with redis

when I try to startRecording using the server-sdk-js and no recorders are available, I get a huge error being thrown by axios, that cannot be catched.

here's a snip of the code I use

'use strict';
const lksdk = require('livekit-server-sdk');
const RecordingServiceClient = require("livekit-server-sdk/dist/RecordingServiceClient").default
const RecordingTemplate = require('livekit-server-sdk/dist/proto/livekit_recording').RecordingTemplate;
const recService = new RecordingServiceClient("http://livekit:7880", process.env.LIVEKIT_API_KEY, process.env.LIVEKIT_API_SECRET);

async function startRecording(roomName) {
  const filename = roomName + ".mp4";
  const tpl = RecordingTemplate.fromJSON({
    layout: "grid-dark",
    roomName: roomName,
  });

  try {
    await recService.startRecording(tpl, filename);
  } catch (e) {
    console.log("error recording:", e);
  }
}

the error being thrown by axios is not catched at all, even though I startRecording() in a try/catch block

here's the complete error I get (it's huge)
 /app/node_modules/axios/lib/core/createError.js:16
   var error = new Error(message);
               ^

 Error: Request failed with status code 500
     at createError (/app/node_modules/axios/lib/core/createError.js:16:15)
     at settle (/app/node_modules/axios/lib/core/settle.js:17:12)
     at IncomingMessage.handleStreamEnd (/app/node_modules/axios/lib/adapters/http.js:269:11)
     at IncomingMessage.emit (node:events:402:35)
     at endReadableNT (node:internal/streams/readable:1343:12)
     at processTicksAndRejections (node:internal/process/task_queues:83:21) {
   config: {
     url: '/twirp/livekit.RecordingService/StartRecording',
     method: 'post',
     data: '{"template":{"layout":"grid-dark","roomName":"NT0C5SD2HCWS5KO8SHJEO","baseUrl":""},"filepath":"NT0C5SD2HCWS5KO8SHJEO.mp4"}',
     headers: {
       Accept: 'application/json, text/plain, */*',
       'Content-Type': 'application/json',
       Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2aWRlbyI6eyJyb29tUmVjb3JkIjp0cnVlfSwiaWF0IjoxNjQ2MjkyNDU4LCJuYmYiOjE2NDYyOTI0NTgsImV4cCI6MTY0NjI5MzA1OCwiaXNzIjoiQVBJRU5XRnRSYkxWY2NrIn0.eBFy0hg4Ss18kR8OWWcImdpAo1jeVeRXxa4DsGTH6I0',
       'User-Agent': 'axios/0.21.4',
       'Content-Length': 122
     },
     baseURL: 'http://livekit:7880',
     transformRequest: [ [Function: transformRequest] ],
     transformResponse: [ [Function: transformResponse] ],
     timeout: 0,
     adapter: [Function: httpAdapter],
     xsrfCookieName: 'XSRF-TOKEN',
     xsrfHeaderName: 'X-XSRF-TOKEN',
     maxContentLength: -1,
     maxBodyLength: -1,
     validateStatus: [Function: validateStatus],
     transitional: {
       silentJSONParsing: true,
       forcedJSONParsing: true,
       clarifyTimeoutError: false
     }
   },
   request: <ref *1> ClientRequest {
     _events: [Object: null prototype] {
       abort: [Function (anonymous)],
       aborted: [Function (anonymous)],
       connect: [Function (anonymous)],
       error: [Function (anonymous)],
       socket: [Function (anonymous)],
       timeout: [Function (anonymous)],
       prefinish: [Function: requestOnPrefinish]
     },
     _eventsCount: 7,
     _maxListeners: undefined,
     outputData: [],
     outputSize: 0,
     writable: true,
     destroyed: false,
     _last: true,
     chunkedEncoding: false,
     shouldKeepAlive: false,
     maxRequestsOnConnectionReached: false,
     _defaultKeepAlive: true,
     useChunkedEncodingByDefault: true,
     sendDate: false,
     _removedConnection: false,
     _removedContLen: false,
     _removedTE: false,
     _contentLength: null,
     _hasBody: true,
     _trailer: '',
     finished: true,
     _headerSent: true,
     _closed: false,
     socket: <ref *2> Socket {
       connecting: false,
       _hadError: false,
       _parent: null,
       _host: 'livekit',
       _readableState: ReadableState {
         objectMode: false,
         highWaterMark: 16384,
         buffer: BufferList { head: null, tail: null, length: 0 },
         length: 0,
         pipes: [],
         flowing: true,
         ended: false,
         endEmitted: false,
         reading: true,
         constructed: true,
         sync: false,
         needReadable: true,
         emittedReadable: false,
         readableListening: false,
         resumeScheduled: false,
         errorEmitted: false,
         emitClose: false,
         autoDestroy: true,
         destroyed: false,
         errored: null,
         closed: false,
         closeEmitted: false,
         defaultEncoding: 'utf8',
         awaitDrainWriters: null,
         multiAwaitDrain: false,
         readingMore: false,
         dataEmitted: true,
         decoder: null,
         encoding: null,
         [Symbol(kPaused)]: false
       },
       _events: [Object: null prototype] {
         end: [Function: onReadableStreamEnd],
         free: [Function: onFree],
         close: [ [Function: onClose], [Function: socketCloseListener] ],
         timeout: [Function: onTimeout],
         agentRemove: [Function: onRemove],
         error: [Function: socketErrorListener],
         finish: [Function: bound onceWrapper] { listener: [Function: destroy] }
       },
       _eventsCount: 7,
       _maxListeners: undefined,
       _writableState: WritableState {
         objectMode: false,
         highWaterMark: 16384,
         finalCalled: true,
         needDrain: false,
         ending: true,
         ended: true,
         finished: false,
         destroyed: false,
         decodeStrings: false,
         defaultEncoding: 'utf8',
         length: 0,
         writing: false,
         corked: 0,
         sync: false,
         bufferProcessing: false,
         onwrite: [Function: bound onwrite],
         writecb: null,
         writelen: 0,
         afterWriteTickInfo: null,
         buffered: [],
         bufferedIndex: 0,
         allBuffers: true,
         allNoop: true,
         pendingcb: 1,
         constructed: true,
         prefinished: false,
         errorEmitted: false,
         emitClose: false,
         autoDestroy: true,
         errored: null,
         closed: false,
         closeEmitted: false,
         [Symbol(kOnFinished)]: []
       },
       allowHalfOpen: false,
       _sockname: null,
       _pendingData: null,
       _pendingEncoding: '',
       server: null,
       _server: null,
       parser: null,
       _httpMessage: [Circular *1],
       [Symbol(async_id_symbol)]: 282,
       [Symbol(kHandle)]: TCP {
         reading: true,
         onconnection: null,
         [Symbol(owner_symbol)]: [Circular *2]
       },
       [Symbol(kSetNoDelay)]: false,
       [Symbol(lastWriteQueueSize)]: 0,
       [Symbol(timeout)]: null,
       [Symbol(kBuffer)]: null,
       [Symbol(kBufferCb)]: null,
       [Symbol(kBufferGen)]: null,
       [Symbol(kCapture)]: false,
       [Symbol(kBytesRead)]: 0,
       [Symbol(kBytesWritten)]: 0,
       [Symbol(RequestTimeout)]: undefined
     },
     _header: 'POST /twirp/livekit.RecordingService/StartRecording HTTP/1.1\r\n' +
       'Accept: application/json, text/plain, */*\r\n' +
       'Content-Type: application/json\r\n' +
       'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2aWRlbyI6eyJyb29tUmVjb3JkIjp0cnVlfSwiaWF0IjoxNjQ2MjkyNDU4LCJuYmYiOjE2NDYyOTI0NTgsImV4cCI6MTY0NjI5MzA1OCwiaXNzIjoiQVBJRU5XRnRSYkxWY2NrIn0.eBFy0hg4Ss18kR8OWWcImdpAo1jeVeRXxa4DsGTH6I0\r\n' +
       'User-Agent: axios/0.21.4\r\n' +
       'Content-Length: 122\r\n' +
       'Host: livekit:7880\r\n' +
       'Connection: close\r\n' +
       '\r\n',
     _keepAliveTimeout: 0,
     _onPendingData: [Function: nop],
     agent: Agent {
       _events: [Object: null prototype] {
         free: [Function (anonymous)],
         newListener: [Function: maybeEnableKeylog]
       },
       _eventsCount: 2,
       _maxListeners: undefined,
       defaultPort: 80,
       protocol: 'http:',
       options: [Object: null prototype] { path: null },
       requests: [Object: null prototype] {},
       sockets: [Object: null prototype] {
         'livekit:7880:': [
           <ref *2> Socket {
             connecting: false,
             _hadError: false,
             _parent: null,
             _host: 'livekit',
             _readableState: [ReadableState],
             _events: [Object: null prototype],
             _eventsCount: 7,
             _maxListeners: undefined,
             _writableState: [WritableState],
             allowHalfOpen: false,
             _sockname: null,
             _pendingData: null,
             _pendingEncoding: '',
             server: null,
             _server: null,
             parser: null,
             _httpMessage: [Circular *1],
             [Symbol(async_id_symbol)]: 282,
             [Symbol(kHandle)]: [TCP],
             [Symbol(kSetNoDelay)]: false,
             [Symbol(lastWriteQueueSize)]: 0,
             [Symbol(timeout)]: null,
             [Symbol(kBuffer)]: null,
             [Symbol(kBufferCb)]: null,
             [Symbol(kBufferGen)]: null,
             [Symbol(kCapture)]: false,
             [Symbol(kBytesRead)]: 0,
             [Symbol(kBytesWritten)]: 0,
             [Symbol(RequestTimeout)]: undefined
           }
         ]
       },
       freeSockets: [Object: null prototype] {},
       keepAliveMsecs: 1000,
       keepAlive: false,
       maxSockets: Infinity,
       maxFreeSockets: 256,
       scheduling: 'lifo',
       maxTotalSockets: Infinity,
       totalSocketCount: 1,
       [Symbol(kCapture)]: false
     },
     socketPath: undefined,
     method: 'POST',
     maxHeaderSize: undefined,
     insecureHTTPParser: undefined,
     path: '/twirp/livekit.RecordingService/StartRecording',
     _ended: true,
     res: IncomingMessage {
       _readableState: ReadableState {
         objectMode: false,
         highWaterMark: 16384,
         buffer: BufferList { head: null, tail: null, length: 0 },
         length: 0,
         pipes: [],
         flowing: true,
         ended: true,
         endEmitted: true,
         reading: false,
         constructed: true,
         sync: true,
         needReadable: false,
         emittedReadable: false,
         readableListening: false,
         resumeScheduled: false,
         errorEmitted: false,
         emitClose: true,
         autoDestroy: true,
         destroyed: true,
         errored: null,
         closed: true,
         closeEmitted: true,
         defaultEncoding: 'utf8',
         awaitDrainWriters: null,
         multiAwaitDrain: false,
         readingMore: true,
         dataEmitted: true,
         decoder: null,
         encoding: null,
         [Symbol(kPaused)]: false
       },
       _events: [Object: null prototype] {
         end: [ [Function: responseOnEnd], [Function: handleStreamEnd] ],
         data: [Function: handleStreamData],
         error: [Function: handleStreamError]
       },
       _eventsCount: 3,
       _maxListeners: undefined,
       socket: <ref *2> Socket {
         connecting: false,
         _hadError: false,
         _parent: null,
         _host: 'livekit',
         _readableState: ReadableState {
           objectMode: false,
           highWaterMark: 16384,
           buffer: BufferList { head: null, tail: null, length: 0 },
           length: 0,
           pipes: [],
           flowing: true,
           ended: false,
           endEmitted: false,
           reading: true,
           constructed: true,
           sync: false,
           needReadable: true,
           emittedReadable: false,
           readableListening: false,
           resumeScheduled: false,
           errorEmitted: false,
           emitClose: false,
           autoDestroy: true,
           destroyed: false,
           errored: null,
           closed: false,
           closeEmitted: false,
           defaultEncoding: 'utf8',
           awaitDrainWriters: null,
           multiAwaitDrain: false,
           readingMore: false,
           dataEmitted: true,
           decoder: null,
           encoding: null,
           [Symbol(kPaused)]: false
         },
         _events: [Object: null prototype] {
           end: [Function: onReadableStreamEnd],
           free: [Function: onFree],
           close: [ [Function: onClose], [Function: socketCloseListener] ],
           timeout: [Function: onTimeout],
           agentRemove: [Function: onRemove],
           error: [Function: socketErrorListener],
           finish: [Function: bound onceWrapper] {
             listener: [Function: destroy]
           }
         },
         _eventsCount: 7,
         _maxListeners: undefined,
         _writableState: WritableState {
           objectMode: false,
           highWaterMark: 16384,
           finalCalled: true,
           needDrain: false,
           ending: true,
           ended: true,
           finished: false,
           destroyed: false,
           decodeStrings: false,
           defaultEncoding: 'utf8',
           length: 0,
           writing: false,
           corked: 0,
           sync: false,
           bufferProcessing: false,
           onwrite: [Function: bound onwrite],
           writecb: null,
           writelen: 0,
           afterWriteTickInfo: null,
           buffered: [],
           bufferedIndex: 0,
           allBuffers: true,
           allNoop: true,
           pendingcb: 1,
           constructed: true,
           prefinished: false,
           errorEmitted: false,
           emitClose: false,
           autoDestroy: true,
           errored: null,
           closed: false,
           closeEmitted: false,
           [Symbol(kOnFinished)]: []
         },
         allowHalfOpen: false,
         _sockname: null,
         _pendingData: null,
         _pendingEncoding: '',
         server: null,
         _server: null,
         parser: null,
         _httpMessage: [Circular *1],
         [Symbol(async_id_symbol)]: 282,
         [Symbol(kHandle)]: TCP {
           reading: true,
           onconnection: null,
           [Symbol(owner_symbol)]: [Circular *2]
         },
         [Symbol(kSetNoDelay)]: false,
         [Symbol(lastWriteQueueSize)]: 0,
         [Symbol(timeout)]: null,
         [Symbol(kBuffer)]: null,
         [Symbol(kBufferCb)]: null,
         [Symbol(kBufferGen)]: null,
         [Symbol(kCapture)]: false,
         [Symbol(kBytesRead)]: 0,
         [Symbol(kBytesWritten)]: 0,
         [Symbol(RequestTimeout)]: undefined
       },
       httpVersionMajor: 1,
       httpVersionMinor: 1,
       httpVersion: '1.1',
       complete: true,
       rawHeaders: [
         'Content-Length',
         '89',
         'Content-Type',
         'application/json',
         'Vary',
         'Origin',
         'Date',
         'Thu, 03 Mar 2022 07:27:41 GMT',
         'Connection',
         'close'
       ],
       rawTrailers: [],
       aborted: false,
       upgrade: false,
       url: '',
       method: null,
       statusCode: 500,
       statusMessage: 'Internal Server Error',
       client: <ref *2> Socket {
         connecting: false,
         _hadError: false,
         _parent: null,
         _host: 'livekit',
         _readableState: ReadableState {
           objectMode: false,
           highWaterMark: 16384,
           buffer: BufferList { head: null, tail: null, length: 0 },
           length: 0,
           pipes: [],
           flowing: true,
           ended: false,
           endEmitted: false,
           reading: true,
           constructed: true,
           sync: false,
           needReadable: true,
           emittedReadable: false,
           readableListening: false,
           resumeScheduled: false,
           errorEmitted: false,
           emitClose: false,
           autoDestroy: true,
           destroyed: false,
           errored: null,
           closed: false,
           closeEmitted: false,
           defaultEncoding: 'utf8',
           awaitDrainWriters: null,
           multiAwaitDrain: false,
           readingMore: false,
           dataEmitted: true,
           decoder: null,
           encoding: null,
           [Symbol(kPaused)]: false
         },
         _events: [Object: null prototype] {
           end: [Function: onReadableStreamEnd],
           free: [Function: onFree],
           close: [ [Function: onClose], [Function: socketCloseListener] ],
           timeout: [Function: onTimeout],
           agentRemove: [Function: onRemove],
           error: [Function: socketErrorListener],
           finish: [Function: bound onceWrapper] {
             listener: [Function: destroy]
           }
         },
         _eventsCount: 7,
         _maxListeners: undefined,
         _writableState: WritableState {
           objectMode: false,
           highWaterMark: 16384,
           finalCalled: true,
           needDrain: false,
           ending: true,
           ended: true,
           finished: false,
           destroyed: false,
           decodeStrings: false,
           defaultEncoding: 'utf8',
           length: 0,
           writing: false,
           corked: 0,
           sync: false,
           bufferProcessing: false,
           onwrite: [Function: bound onwrite],
           writecb: null,
           writelen: 0,
           afterWriteTickInfo: null,
           buffered: [],
           bufferedIndex: 0,
           allBuffers: true,
           allNoop: true,
           pendingcb: 1,
           constructed: true,
           prefinished: false,
           errorEmitted: false,
           emitClose: false,
           autoDestroy: true,
           errored: null,
           closed: false,
           closeEmitted: false,
           [Symbol(kOnFinished)]: []
         },
         allowHalfOpen: false,
         _sockname: null,
         _pendingData: null,
         _pendingEncoding: '',
         server: null,
         _server: null,
         parser: null,
         _httpMessage: [Circular *1],
         [Symbol(async_id_symbol)]: 282,
         [Symbol(kHandle)]: TCP {
           reading: true,
           onconnection: null,
           [Symbol(owner_symbol)]: [Circular *2]
         },
         [Symbol(kSetNoDelay)]: false,
         [Symbol(lastWriteQueueSize)]: 0,
         [Symbol(timeout)]: null,
         [Symbol(kBuffer)]: null,
         [Symbol(kBufferCb)]: null,
         [Symbol(kBufferGen)]: null,
         [Symbol(kCapture)]: false,
         [Symbol(kBytesRead)]: 0,
         [Symbol(kBytesWritten)]: 0,
         [Symbol(RequestTimeout)]: undefined
       },
       _consuming: false,
       _dumped: false,
       req: [Circular *1],
       responseUrl: 'http://livekit:7880/twirp/livekit.RecordingService/StartRecording',
       redirects: [],
       [Symbol(kCapture)]: false,
       [Symbol(kHeaders)]: {
         'content-length': '89',
         'content-type': 'application/json',
         vary: 'Origin',
         date: 'Thu, 03 Mar 2022 07:27:41 GMT',
         connection: 'close'
       },
       [Symbol(kHeadersCount)]: 10,
       [Symbol(kTrailers)]: null,
       [Symbol(kTrailersCount)]: 0,
       [Symbol(RequestTimeout)]: undefined
     },
     aborted: false,
     timeoutCb: null,
     upgradeOrConnect: false,
     parser: null,
     maxHeadersCount: null,
     reusedSocket: false,
     host: 'livekit',
     protocol: 'http:',
     _redirectable: Writable {
       _writableState: WritableState {
         objectMode: false,
         highWaterMark: 16384,
         finalCalled: false,
         needDrain: false,
         ending: false,
         ended: false,
         finished: false,
         destroyed: false,
         decodeStrings: true,
         defaultEncoding: 'utf8',
         length: 0,
         writing: false,
         corked: 0,
         sync: true,
         bufferProcessing: false,
         onwrite: [Function: bound onwrite],
         writecb: null,
         writelen: 0,
         afterWriteTickInfo: null,
         buffered: [],
         bufferedIndex: 0,
         allBuffers: true,
         allNoop: true,
         pendingcb: 0,
         constructed: true,
         prefinished: false,
         errorEmitted: false,
         emitClose: true,
         autoDestroy: true,
         errored: null,
         closed: false,
         closeEmitted: false,
         [Symbol(kOnFinished)]: []
       },
       _events: [Object: null prototype] {
         response: [Function: handleResponse],
         error: [Function: handleRequestError]
       },
       _eventsCount: 2,
       _maxListeners: undefined,
       _options: {
         maxRedirects: 21,
         maxBodyLength: 10485760,
         protocol: 'http:',
         path: '/twirp/livekit.RecordingService/StartRecording',
         method: 'POST',
         headers: {
           Accept: 'application/json, text/plain, */*',
           'Content-Type': 'application/json',
           Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2aWRlbyI6eyJyb29tUmVjb3JkIjp0cnVlfSwiaWF0IjoxNjQ2MjkyNDU4LCJuYmYiOjE2NDYyOTI0NTgsImV4cCI6MTY0NjI5MzA1OCwiaXNzIjoiQVBJRU5XRnRSYkxWY2NrIn0.eBFy0hg4Ss18kR8OWWcImdpAo1jeVeRXxa4DsGTH6I0',
           'User-Agent': 'axios/0.21.4',
           'Content-Length': 122
         },
         agent: undefined,
         agents: { http: undefined, https: undefined },
         auth: undefined,
         hostname: 'livekit',
         port: '7880',
         nativeProtocols: {
           'http:': {
             _connectionListener: [Function: connectionListener],
             METHODS: [Array],
             STATUS_CODES: [Object],
             Agent: [Function],
             ClientRequest: [Function: ClientRequest],
             IncomingMessage: [Function: IncomingMessage],
             OutgoingMessage: [Function: OutgoingMessage],
             Server: [Function: Server],
             ServerResponse: [Function: ServerResponse],
             createServer: [Function: createServer],
             validateHeaderName: [Function: __node_internal_],
             validateHeaderValue: [Function: __node_internal_],
             get: [Function: get],
             request: [Function: request],
             maxHeaderSize: [Getter],
             globalAgent: [Getter/Setter]
           },
           'https:': {
             Agent: [Function: Agent],
             globalAgent: [Agent],
             Server: [Function: Server],
             createServer: [Function: createServer],
             get: [Function: get],
             request: [Function: request]
           }
         },
         pathname: '/twirp/livekit.RecordingService/StartRecording'
       },
       _ended: true,
       _ending: true,
       _redirectCount: 0,
       _redirects: [],
       _requestBodyLength: 122,
       _requestBodyBuffers: [],
       _onNativeResponse: [Function (anonymous)],
       _currentRequest: [Circular *1],
       _currentUrl: 'http://livekit:7880/twirp/livekit.RecordingService/StartRecording',
       [Symbol(kCapture)]: false
     },
     [Symbol(kCapture)]: false,
     [Symbol(kNeedDrain)]: false,
     [Symbol(corked)]: 0,
     [Symbol(kOutHeaders)]: [Object: null prototype] {
       accept: [ 'Accept', 'application/json, text/plain, */*' ],
       'content-type': [ 'Content-Type', 'application/json' ],
       authorization: [
         'Authorization',
         'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2aWRlbyI6eyJyb29tUmVjb3JkIjp0cnVlfSwiaWF0IjoxNjQ2MjkyNDU4LCJuYmYiOjE2NDYyOTI0NTgsImV4cCI6MTY0NjI5MzA1OCwiaXNzIjoiQVBJRU5XRnRSYkxWY2NrIn0.eBFy0hg4Ss18kR8OWWcImdpAo1jeVeRXxa4DsGTH6I0'
       ],
       'user-agent': [ 'User-Agent', 'axios/0.21.4' ],
       'content-length': [ 'Content-Length', 122 ],
       host: [ 'Host', 'livekit:7880' ]
     }
   },
   response: {
     status: 500,
     statusText: 'Internal Server Error',
     headers: {
       'content-length': '89',
       'content-type': 'application/json',
       vary: 'Origin',
       date: 'Thu, 03 Mar 2022 07:27:41 GMT',
       connection: 'close'
     },
     config: {
       url: '/twirp/livekit.RecordingService/StartRecording',
       method: 'post',
       data: '{"template":{"layout":"grid-dark","roomName":"NT0C5SD2HCWS5KO8SHJEO","baseUrl":""},"filepath":"NT0C5SD2HCWS5KO8SHJEO.mp4"}',
       headers: {
         Accept: 'application/json, text/plain, */*',
         'Content-Type': 'application/json',
         Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2aWRlbyI6eyJyb29tUmVjb3JkIjp0cnVlfSwiaWF0IjoxNjQ2MjkyNDU4LCJuYmYiOjE2NDYyOTI0NTgsImV4cCI6MTY0NjI5MzA1OCwiaXNzIjoiQVBJRU5XRnRSYkxWY2NrIn0.eBFy0hg4Ss18kR8OWWcImdpAo1jeVeRXxa4DsGTH6I0',
         'User-Agent': 'axios/0.21.4',
         'Content-Length': 122
       },
       baseURL: 'http://livekit:7880',
       transformRequest: [ [Function: transformRequest] ],
       transformResponse: [ [Function: transformResponse] ],
       timeout: 0,
       adapter: [Function: httpAdapter],
       xsrfCookieName: 'XSRF-TOKEN',
       xsrfHeaderName: 'X-XSRF-TOKEN',
       maxContentLength: -1,
       maxBodyLength: -1,
       validateStatus: [Function: validateStatus],
       transitional: {
         silentJSONParsing: true,
         forcedJSONParsing: true,
         clarifyTimeoutError: false
       }
     },
     request: <ref *1> ClientRequest {
       _events: [Object: null prototype] {
         abort: [Function (anonymous)],
         aborted: [Function (anonymous)],
         connect: [Function (anonymous)],
         error: [Function (anonymous)],
         socket: [Function (anonymous)],
         timeout: [Function (anonymous)],
         prefinish: [Function: requestOnPrefinish]
       },
       _eventsCount: 7,
       _maxListeners: undefined,
       outputData: [],
       outputSize: 0,
       writable: true,
       destroyed: false,
       _last: true,
       chunkedEncoding: false,
       shouldKeepAlive: false,
       maxRequestsOnConnectionReached: false,
       _defaultKeepAlive: true,
       useChunkedEncodingByDefault: true,
       sendDate: false,
       _removedConnection: false,
       _removedContLen: false,
       _removedTE: false,
       _contentLength: null,
       _hasBody: true,
       _trailer: '',
       finished: true,
       _headerSent: true,
       _closed: false,
       socket: <ref *2> Socket {
         connecting: false,
         _hadError: false,
         _parent: null,
         _host: 'livekit',
         _readableState: ReadableState {
           objectMode: false,
           highWaterMark: 16384,
           buffer: BufferList { head: null, tail: null, length: 0 },
           length: 0,
           pipes: [],
           flowing: true,
           ended: false,
           endEmitted: false,
           reading: true,
           constructed: true,
           sync: false,
           needReadable: true,
           emittedReadable: false,
           readableListening: false,
           resumeScheduled: false,
           errorEmitted: false,
           emitClose: false,
           autoDestroy: true,
           destroyed: false,
           errored: null,
           closed: false,
           closeEmitted: false,
           defaultEncoding: 'utf8',
           awaitDrainWriters: null,
           multiAwaitDrain: false,
           readingMore: false,
           dataEmitted: true,
           decoder: null,
           encoding: null,
           [Symbol(kPaused)]: false
         },
         _events: [Object: null prototype] {
           end: [Function: onReadableStreamEnd],
           free: [Function: onFree],
           close: [ [Function: onClose], [Function: socketCloseListener] ],
           timeout: [Function: onTimeout],
           agentRemove: [Function: onRemove],
           error: [Function: socketErrorListener],
           finish: [Function: bound onceWrapper] {
             listener: [Function: destroy]
           }
         },
         _eventsCount: 7,
         _maxListeners: undefined,
         _writableState: WritableState {
           objectMode: false,
           highWaterMark: 16384,
           finalCalled: true,
           needDrain: false,
           ending: true,
           ended: true,
           finished: false,
           destroyed: false,
           decodeStrings: false,
           defaultEncoding: 'utf8',
           length: 0,
           writing: false,
           corked: 0,
           sync: false,
           bufferProcessing: false,
           onwrite: [Function: bound onwrite],
           writecb: null,
           writelen: 0,
           afterWriteTickInfo: null,
           buffered: [],
           bufferedIndex: 0,
           allBuffers: true,
           allNoop: true,
           pendingcb: 1,
           constructed: true,
           prefinished: false,
           errorEmitted: false,
           emitClose: false,
           autoDestroy: true,
           errored: null,
           closed: false,
           closeEmitted: false,
           [Symbol(kOnFinished)]: []
         },
         allowHalfOpen: false,
         _sockname: null,
         _pendingData: null,
         _pendingEncoding: '',
         server: null,
         _server: null,
         parser: null,
         _httpMessage: [Circular *1],
         [Symbol(async_id_symbol)]: 282,
         [Symbol(kHandle)]: TCP {
           reading: true,
           onconnection: null,
           [Symbol(owner_symbol)]: [Circular *2]
         },
         [Symbol(kSetNoDelay)]: false,
         [Symbol(lastWriteQueueSize)]: 0,
         [Symbol(timeout)]: null,
         [Symbol(kBuffer)]: null,
         [Symbol(kBufferCb)]: null,
         [Symbol(kBufferGen)]: null,
         [Symbol(kCapture)]: false,
         [Symbol(kBytesRead)]: 0,
         [Symbol(kBytesWritten)]: 0,
         [Symbol(RequestTimeout)]: undefined
       },
       _header: 'POST /twirp/livekit.RecordingService/StartRecording HTTP/1.1\r\n' +
         'Accept: application/json, text/plain, */*\r\n' +
         'Content-Type: application/json\r\n' +
         'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2aWRlbyI6eyJyb29tUmVjb3JkIjp0cnVlfSwiaWF0IjoxNjQ2MjkyNDU4LCJuYmYiOjE2NDYyOTI0NTgsImV4cCI6MTY0NjI5MzA1OCwiaXNzIjoiQVBJRU5XRnRSYkxWY2NrIn0.eBFy0hg4Ss18kR8OWWcImdpAo1jeVeRXxa4DsGTH6I0\r\n' +
         'User-Agent: axios/0.21.4\r\n' +
         'Content-Length: 122\r\n' +
         'Host: livekit:7880\r\n' +
         'Connection: close\r\n' +
         '\r\n',
       _keepAliveTimeout: 0,
       _onPendingData: [Function: nop],
       agent: Agent {
         _events: [Object: null prototype] {
           free: [Function (anonymous)],
           newListener: [Function: maybeEnableKeylog]
         },
         _eventsCount: 2,
         _maxListeners: undefined,
         defaultPort: 80,
         protocol: 'http:',
         options: [Object: null prototype] { path: null },
         requests: [Object: null prototype] {},
         sockets: [Object: null prototype] { 'livekit:7880:': [ [Socket] ] },
         freeSockets: [Object: null prototype] {},
         keepAliveMsecs: 1000,
         keepAlive: false,
         maxSockets: Infinity,
         maxFreeSockets: 256,
         scheduling: 'lifo',
         maxTotalSockets: Infinity,
         totalSocketCount: 1,
         [Symbol(kCapture)]: false
       },
       socketPath: undefined,
       method: 'POST',
       maxHeaderSize: undefined,
       insecureHTTPParser: undefined,
       path: '/twirp/livekit.RecordingService/StartRecording',
       _ended: true,
       res: IncomingMessage {
         _readableState: ReadableState {
           objectMode: false,
           highWaterMark: 16384,
           buffer: BufferList { head: null, tail: null, length: 0 },
           length: 0,
           pipes: [],
           flowing: true,
           ended: true,
           endEmitted: true,
           reading: false,
           constructed: true,
           sync: true,
           needReadable: false,
           emittedReadable: false,
           readableListening: false,
           resumeScheduled: false,
           errorEmitted: false,
           emitClose: true,
           autoDestroy: true,
           destroyed: true,
           errored: null,
           closed: true,
           closeEmitted: true,
           defaultEncoding: 'utf8',
           awaitDrainWriters: null,
           multiAwaitDrain: false,
           readingMore: true,
           dataEmitted: true,
           decoder: null,
           encoding: null,
           [Symbol(kPaused)]: false
         },
         _events: [Object: null prototype] {
           end: [ [Function: responseOnEnd], [Function: handleStreamEnd] ],
           data: [Function: handleStreamData],
           error: [Function: handleStreamError]
         },
         _eventsCount: 3,
         _maxListeners: undefined,
         socket: <ref *2> Socket {
           connecting: false,
           _hadError: false,
           _parent: null,
           _host: 'livekit',
           _readableState: ReadableState {
             objectMode: false,
             highWaterMark: 16384,
             buffer: [BufferList],
             length: 0,
             pipes: [],
             flowing: true,
             ended: false,
             endEmitted: false,
             reading: true,
             constructed: true,
             sync: false,
             needReadable: true,
             emittedReadable: false,
             readableListening: false,
             resumeScheduled: false,
             errorEmitted: false,
             emitClose: false,
             autoDestroy: true,
             destroyed: false,
             errored: null,
             closed: false,
             closeEmitted: false,
             defaultEncoding: 'utf8',
             awaitDrainWriters: null,
             multiAwaitDrain: false,
             readingMore: false,
             dataEmitted: true,
             decoder: null,
             encoding: null,
             [Symbol(kPaused)]: false
           },
           _events: [Object: null prototype] {
             end: [Function: onReadableStreamEnd],
             free: [Function: onFree],
             close: [Array],
             timeout: [Function: onTimeout],
             agentRemove: [Function: onRemove],
             error: [Function: socketErrorListener],
             finish: [Function]
           },
           _eventsCount: 7,
           _maxListeners: undefined,
           _writableState: WritableState {
             objectMode: false,
             highWaterMark: 16384,
             finalCalled: true,
             needDrain: false,
             ending: true,
             ended: true,
             finished: false,
             destroyed: false,
             decodeStrings: false,
             defaultEncoding: 'utf8',
             length: 0,
             writing: false,
             corked: 0,
             sync: false,
             bufferProcessing: false,
             onwrite: [Function: bound onwrite],
             writecb: null,
             writelen: 0,
             afterWriteTickInfo: null,
             buffered: [],
             bufferedIndex: 0,
             allBuffers: true,
             allNoop: true,
             pendingcb: 1,
             constructed: true,
             prefinished: false,
             errorEmitted: false,
             emitClose: false,
             autoDestroy: true,
             errored: null,
             closed: false,
             closeEmitted: false,
             [Symbol(kOnFinished)]: []
           },
           allowHalfOpen: false,
           _sockname: null,
           _pendingData: null,
           _pendingEncoding: '',
           server: null,
           _server: null,
           parser: null,
           _httpMessage: [Circular *1],
           [Symbol(async_id_symbol)]: 282,
           [Symbol(kHandle)]: TCP {
             reading: true,
             onconnection: null,
             [Symbol(owner_symbol)]: [Circular *2]
           },
           [Symbol(kSetNoDelay)]: false,
           [Symbol(lastWriteQueueSize)]: 0,
           [Symbol(timeout)]: null,
           [Symbol(kBuffer)]: null,
           [Symbol(kBufferCb)]: null,
           [Symbol(kBufferGen)]: null,
           [Symbol(kCapture)]: false,
           [Symbol(kBytesRead)]: 0,
           [Symbol(kBytesWritten)]: 0,
           [Symbol(RequestTimeout)]: undefined
         },
         httpVersionMajor: 1,
         httpVersionMinor: 1,
         httpVersion: '1.1',
         complete: true,
         rawHeaders: [
           'Content-Length',
           '89',
           'Content-Type',
           'application/json',
           'Vary',
           'Origin',
           'Date',
           'Thu, 03 Mar 2022 07:27:41 GMT',
           'Connection',
           'close'
         ],
         rawTrailers: [],
         aborted: false,
         upgrade: false,
         url: '',
         method: null,
         statusCode: 500,
         statusMessage: 'Internal Server Error',
         client: <ref *2> Socket {
           connecting: false,
           _hadError: false,
           _parent: null,
           _host: 'livekit',
           _readableState: ReadableState {
             objectMode: false,
             highWaterMark: 16384,
             buffer: [BufferList],
             length: 0,
             pipes: [],
             flowing: true,
             ended: false,
             endEmitted: false,
             reading: true,
             constructed: true,
             sync: false,
             needReadable: true,
             emittedReadable: false,
             readableListening: false,
             resumeScheduled: false,
             errorEmitted: false,
             emitClose: false,
             autoDestroy: true,
             destroyed: false,
             errored: null,
             closed: false,
             closeEmitted: false,
             defaultEncoding: 'utf8',
             awaitDrainWriters: null,
             multiAwaitDrain: false,
             readingMore: false,
             dataEmitted: true,
             decoder: null,
             encoding: null,
             [Symbol(kPaused)]: false
           },
           _events: [Object: null prototype] {
             end: [Function: onReadableStreamEnd],
             free: [Function: onFree],
             close: [Array],
             timeout: [Function: onTimeout],
             agentRemove: [Function: onRemove],
             error: [Function: socketErrorListener],
             finish: [Function]
           },
           _eventsCount: 7,
           _maxListeners: undefined,
           _writableState: WritableState {
             objectMode: false,
             highWaterMark: 16384,
             finalCalled: true,
             needDrain: false,
             ending: true,
             ended: true,
             finished: false,
             destroyed: false,
             decodeStrings: false,
             defaultEncoding: 'utf8',
             length: 0,
             writing: false,
             corked: 0,
             sync: false,
             bufferProcessing: false,
             onwrite: [Function: bound onwrite],
             writecb: null,
             writelen: 0,
             afterWriteTickInfo: null,
             buffered: [],
             bufferedIndex: 0,
             allBuffers: true,
             allNoop: true,
             pendingcb: 1,
             constructed: true,
             prefinished: false,
             errorEmitted: false,
             emitClose: false,
             autoDestroy: true,
             errored: null,
             closed: false,
             closeEmitted: false,
             [Symbol(kOnFinished)]: []
           },
           allowHalfOpen: false,
           _sockname: null,
           _pendingData: null,
           _pendingEncoding: '',
           server: null,
           _server: null,
           parser: null,
           _httpMessage: [Circular *1],
           [Symbol(async_id_symbol)]: 282,
           [Symbol(kHandle)]: TCP {
             reading: true,
             onconnection: null,
             [Symbol(owner_symbol)]: [Circular *2]
           },
           [Symbol(kSetNoDelay)]: false,
           [Symbol(lastWriteQueueSize)]: 0,
           [Symbol(timeout)]: null,
           [Symbol(kBuffer)]: null,
           [Symbol(kBufferCb)]: null,
           [Symbol(kBufferGen)]: null,
           [Symbol(kCapture)]: false,
           [Symbol(kBytesRead)]: 0,
           [Symbol(kBytesWritten)]: 0,
           [Symbol(RequestTimeout)]: undefined
         },
         _consuming: false,
         _dumped: false,
         req: [Circular *1],
         responseUrl: 'http://livekit:7880/twirp/livekit.RecordingService/StartRecording',
         redirects: [],
         [Symbol(kCapture)]: false,
         [Symbol(kHeaders)]: {
           'content-length': '89',
           'content-type': 'application/json',
           vary: 'Origin',
           date: 'Thu, 03 Mar 2022 07:27:41 GMT',
           connection: 'close'
         },
         [Symbol(kHeadersCount)]: 10,
         [Symbol(kTrailers)]: null,
         [Symbol(kTrailersCount)]: 0,
         [Symbol(RequestTimeout)]: undefined
       },
       aborted: false,
       timeoutCb: null,
       upgradeOrConnect: false,
       parser: null,
       maxHeadersCount: null,
       reusedSocket: false,
       host: 'livekit',
       protocol: 'http:',
       _redirectable: Writable {
         _writableState: WritableState {
           objectMode: false,
           highWaterMark: 16384,
           finalCalled: false,
           needDrain: false,
           ending: false,
           ended: false,
           finished: false,
           destroyed: false,
           decodeStrings: true,
           defaultEncoding: 'utf8',
           length: 0,
           writing: false,
           corked: 0,
           sync: true,
           bufferProcessing: false,
           onwrite: [Function: bound onwrite],
           writecb: null,
           writelen: 0,
           afterWriteTickInfo: null,
           buffered: [],
           bufferedIndex: 0,
           allBuffers: true,
           allNoop: true,
           pendingcb: 0,
           constructed: true,
           prefinished: false,
           errorEmitted: false,
           emitClose: true,
           autoDestroy: true,
           errored: null,
           closed: false,
           closeEmitted: false,
           [Symbol(kOnFinished)]: []
         },
         _events: [Object: null prototype] {
           response: [Function: handleResponse],
           error: [Function: handleRequestError]
         },
         _eventsCount: 2,
         _maxListeners: undefined,
         _options: {
           maxRedirects: 21,
           maxBodyLength: 10485760,
           protocol: 'http:',
           path: '/twirp/livekit.RecordingService/StartRecording',
           method: 'POST',
           headers: {
             Accept: 'application/json, text/plain, */*',
             'Content-Type': 'application/json',
             Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2aWRlbyI6eyJyb29tUmVjb3JkIjp0cnVlfSwiaWF0IjoxNjQ2MjkyNDU4LCJuYmYiOjE2NDYyOTI0NTgsImV4cCI6MTY0NjI5MzA1OCwiaXNzIjoiQVBJRU5XRnRSYkxWY2NrIn0.eBFy0hg4Ss18kR8OWWcImdpAo1jeVeRXxa4DsGTH6I0',
             'User-Agent': 'axios/0.21.4',
             'Content-Length': 122
           },
           agent: undefined,
           agents: { http: undefined, https: undefined },
           auth: undefined,
           hostname: 'livekit',
           port: '7880',
           nativeProtocols: { 'http:': [Object], 'https:': [Object] },
           pathname: '/twirp/livekit.RecordingService/StartRecording'
         },
         _ended: true,
         _ending: true,
         _redirectCount: 0,
         _redirects: [],
         _requestBodyLength: 122,
         _requestBodyBuffers: [],
         _onNativeResponse: [Function (anonymous)],
         _currentRequest: [Circular *1],
         _currentUrl: 'http://livekit:7880/twirp/livekit.RecordingService/StartRecording',
         [Symbol(kCapture)]: false
       },
       [Symbol(kCapture)]: false,
       [Symbol(kNeedDrain)]: false,
       [Symbol(corked)]: 0,
       [Symbol(kOutHeaders)]: [Object: null prototype] {
         accept: [ 'Accept', 'application/json, text/plain, */*' ],
         'content-type': [ 'Content-Type', 'application/json' ],
         authorization: [
           'Authorization',
           'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2aWRlbyI6eyJyb29tUmVjb3JkIjp0cnVlfSwiaWF0IjoxNjQ2MjkyNDU4LCJuYmYiOjE2NDYyOTI0NTgsImV4cCI6MTY0NjI5MzA1OCwiaXNzIjoiQVBJRU5XRnRSYkxWY2NrIn0.eBFy0hg4Ss18kR8OWWcImdpAo1jeVeRXxa4DsGTH6I0'
         ],
         'user-agent': [ 'User-Agent', 'axios/0.21.4' ],
         'content-length': [ 'Content-Length', 122 ],
         host: [ 'Host', 'livekit:7880' ]
       }
     },
     data: {
       code: 'internal',
       msg: 'no recorders available',
       meta: { cause: '*errors.errorString' }
     }
   },
   isAxiosError: true,
   toJSON: [Function: toJSON]
 }

Using ingress breaks livekit.cloud project

I'm trying to get the https://github.com/livekit-examples/livestream project to work locally, and came across a weird issue.

I can't get a stream started with the given Ingress presets, so I tried removing them to see if that would let me stream from OBS. Now, when I try create a RMTP URL I get a status 500 info (nothing useful in the response), and from that point forward any new API keys I generate are useless. To get new, working keys I need to create a new project on cloud.livekit.io

wait - compiling /api/trpc/[trpc] (client and server)...
event - compiled successfully in 56 ms (131 modules)
❌ tRPC failed on ingress.create: Request failed with status code 500

  const ingress = await ingressClient.createIngress(
    IngressInput.RTMP_INPUT,
    {
      name: input.roomSlug,
      roomName: input.roomSlug,
      participantName: input.streamerName,
      participantIdentity: input.roomSlug,
      },
    }
  );

Question about startEgress

Is it guaranteed that startTrackEgress and startTrackCompositeEgress will resolve only after an egress instance successfully started recording(status is started)?

My use case is the following:

  1. I call and await startTrackCompositeEgress()
  2. set {recording: true} on the room metadata
  3. on client side(react) detect this change in metadata and update the UI

Livekit importing err when working with nestjs.

I am getting error

const livekit_server_sdk_1 = require("livekit-server-sdk");
                             ^
Error [ERR_REQUIRE_ESM]: require() of ES Module /home/aldrich/temp/nest/livekit-server/node_modules/.pnpm/[email protected]/node_modules/livekit-server-sdk/dist/index.js from /home/
aldrich/temp/nest/livekit-server/dist/app.service.js not supported.
Instead change the require of index.js in /home/aldrich/temp/nest/livekit-server/dist/app.service.js to a dynamic import() which is available in all CommonJS modules.
    at Object.<anonymous> (/home/aldrich/temp/nest/livekit-server/dist/app.service.js:11:30)
    at Object.<anonymous> (/home/aldrich/temp/nest/livekit-server/dist/app.controller.js:14:23)
    at Object.<anonymous> (/home/aldrich/temp/nest/livekit-server/dist/app.module.js:11:26)
    at Object.<anonymous> (/home/aldrich/temp/nest/livekit-server/dist/main.js:4:22)

when I try to import livekit from a nestjs backend.

Steps for Reproduction:

  1. git clone https://github.com/jossephus/livekit-with-nestjs
  2. cd livekit-with-nestjs && pnpm install
  3. pnpm start:dev

further info: this is the only place I am using Livekit sdk

image

Import error in nestjs typescript

I am integrating livekit-sdk-js in my nestjs project. It is throwing me import error

/home/app/dist/modules/live-stream/services/livekit.services.js:14
const livekit_server_sdk_1 = require("livekit-server-sdk");
                             ^
Error [ERR_REQUIRE_ESM]: require() of ES Module/home/app/node_modules/livekit-server-sdk/dist/index.js from /home/app/dist/modules/live-stream/services/livekit.services.js not supported.
Instead change the require of index.js in /home/app/dist/modules/live-stream/services/livekit.services.js to a dynamic import() which is available in all CommonJS modules.
    at Object.<anonymous> (/home/app/dist/modules/live-stream/services/livekit.services.js:14:30)

Livekit version from package-lock.json:

"node_modules/livekit-server-sdk": {
      "version": "2.0.2",
      "resolved": "https://registry.npmjs.org/livekit-server-sdk/-/livekit-server-sdk-2.0.2.tgz",
      "integrity": "sha512-txRuM8HDXTpzKGdwUwL6uJI7AuOeB+a+ozJNO/OWJhf/xLnYmJn4Vwpzar5+DkW10878pfFpu/NsWiu6j3120g==",
      "dependencies": {
        "@bufbuild/protobuf": "^1.3.0",
        "camelcase-keys": "^7.0.0",
        "jose": "^5.1.2"
      },
      "engines": {
        "node": ">=18"
      }
    },

and tsconfig.json:

{
  "compilerOptions": {
    "module": "commonjs",
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "target": "ES2021",
    "sourceMap": true,
    "outDir": "./dist",
    "baseUrl": "./",
    "incremental": true,
    "skipLibCheck": true,
    "strictNullChecks": false,
    "noImplicitAny": false,
    "strictBindCallApply": false,
    "forceConsistentCasingInFileNames": false,
    "noFallthroughCasesInSwitch": false
  }
}

Text Chat via WebRTC Data Channel is unusable

The chat implementation in your application is not reliable because messages don't get sent if there are no participants. In reality, they should be stored in history so that a new participant can receive the messages. From the backend and webhook side, I don't see a way to control this. Can you provide any guidance?

Protobufjs import & ts compile error

Ran into this issue via a Firebase Functions NodeJS project. I haven't had a chance to try outside of Firebase, but I think it shouldn't be Firebase specific, as it's the TS compiler that is tripping up over an import. I
To repro (assuming you have a firebase account):

  1. Get the firebase sdk npm install -g firebase-tools
  2. Log into the account: firebase login
  3. Create Functions: firebase init functions and choose Typescript. This essentially creates a Node Express project.
  4. Modify the main.ts file with a livekit import import {AccessToken} from "livekit-server-sdk";
  5. Run `npm install' in the functions dir
  6. Add the "Create Access Tokens" example into one of the functions (like "Hello" - doesn't matter which one, as long as the linter is happy).
  7. Run npm run build, which should call the Typescript compiler.

The compiler should complain about the line from various *.d.ts files and the lack of a default export:

import _m0 from "protobufjs/minimal";

I fixed this by manually editing those files to look like:

import * as _m0 from "protobufjs/minimal";

I am on Node 14.17.6 and npm 6.14.15, but I have seen this issue in older and newer versions of both Node and npm.

After upgrading to v2, all functions like `createIngress()` are broken

After following the instructions I get the following error message for all functions:
Error: cannot decode message livekit.IngressInfo from JSON: key "participantMetadata" is unknown

I searched for participantMetadata in the source code but didn't find anything to fix this error.

Full error:

 ⨯ node_modules\@bufbuild\protobuf\dist\cjs\private\json-format-common.js (87:30) @ Object.readMessage
 ⨯ Error: cannot decode message livekit.IngressInfo from JSON: key "participantMetadata" is unknown
    at async resetIngresses (./src/actions/ingress.ts:26:23)
    at async createIngress (./src/actions/ingress.ts:43:5)

Functions which call the error:

// Error: "at async resetIngresses (./src/actions/ingress.ts:26:23)"
const ingresses = await ingressClient.listIngress({
  roomName: hostIdentity
});

// ...
const options: CreateIngressOptions = {
  name: self.name!,
  roomName: self.id,
  participantName: self.name!,
  participantIdentity: self.id
};

options.video = new IngressVideoOptions({
  source: TrackSource.CAMERA,
  encodingOptions: {
    value: IngressVideoEncodingPreset.H264_720P_30FPS_3_LAYERS,
    case: 'preset'
  }
});
options.audio = new IngressAudioOptions({
  source: TrackSource.MICROPHONE,
  encodingOptions: {
    value: IngressAudioEncodingPreset.OPUS_STEREO_96KBPS,
    case: 'preset'
  }
});

// Error: "at async createIngress (./src/actions/ingress.ts:43:5)"
const ingress = await ingressClient.createIngress(
  ingressType,
  options
);

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

github-actions
.github/workflows/release.yaml
  • actions/checkout v4
  • pnpm/action-setup v2
  • actions/setup-node v4
  • changesets/action v1
.github/workflows/test.yaml
  • actions/checkout v4
  • pnpm/action-setup v2
  • actions/setup-node v4
npm
package.json
  • @livekit/protocol ^1.14.0
  • camelcase-keys ^9.0.0
  • jose ^5.1.2
  • @bufbuild/protobuf ^1.7.2
  • @changesets/cli ^2.27.1
  • @edge-runtime/vm ^3.1.7
  • @livekit/changesets-changelog-github ^0.0.4
  • @types/node ^20.10.1
  • @typescript-eslint/eslint-plugin ^7.7.0
  • eslint ^8.56.0
  • eslint-config-airbnb-typescript ^18.0.0
  • eslint-config-prettier ^9.1.0
  • eslint-plugin-import ^2.29.1
  • happy-dom ^14.0.0
  • prettier ^3.0.0
  • typedoc ^0.25.13
  • typescript 5.4.x
  • vite ^5.2.9
  • vitest ^1.5.0
  • node >=19

  • Check this box to trigger a request for Renovate to run again on this repository

Inquiry about compatibility of Live Kit SDK with Deno

Hello,

I'm interested in using the Live Kit SDK in a project that's being developed with Deno, and I was wondering if the SDK is compatible with the Deno runtime environment.

I've searched the documentation and couldn't find any mention of Deno compatibility, so I thought I'd ask here to see if anyone has experience using the SDK with Deno.

If anyone knows whether the Live Kit SDK works with Deno or has any tips or resources for making it work, I'd appreciate any information you can provide.

Thank you!

Cannot find module 'long'

image

Its looks like you use long in src/proto/livekit_ingress.ts but not added into dependencies field in package.json

Did you visit it by shadow module? I cant use it in pnpm

Expose WebhookEvent type

Hi! I have a small quality of live improvement request :).

When using this library from TypeScript, I was trying to write a function which takes a WebhookEvent (result of WebhookReceiver.receive), but I couldn't figure out how to get the type of WebhookEvent easily.

Right now, here's how I am getting it, but as you can see that's a bit of a roundabout way of getting it:

import * as livekit from 'livekit-server-sdk';
type LivekitWebhookEvent = ReturnType<
  InstanceType<typeof livekit.WebhookReceiver>['receive']
>;

I tried import * as livekit from 'livekit-server-sdk/proto but it didn't work for mysterious JavaScript module system reasons.

Nonetheless, I think it would be nice if it WebhookEvent was exported from index.ts :).

Send Data from server-sdk-js

Good Day.

I am trying to send custom message from the server side using the server-sdk-js. As an example code below:

app.get('/publish/:room', function(req, res, next) {
	if (allowedIp.indexOf(req.connection.remoteAddress) >-1)  {
		const encoder = new TextEncoder();
		const data = encoder.encode('hello world');
		const opts = {
			room: 'roomName',
			data: data,
			kind: DataPacket_Kind.RELIABLE,
		};
    svc.sendData(opts).then((_) => {
			res.json(_);
			console.log('Data published', _);
		}).catch((e) => {
	    console.log('Handle error here: ', e.message);
	    next();
	 	});
	} else {
		res.send("Publish fail");
	}
});

However, I am always getting error status 401. From what I can tell, do I need to pass token for the roomAdmin?

Thank you.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.