diff --git a/components/livekit/actions/create-ingress-from-url/create-ingress-from-url.mjs b/components/livekit/actions/create-ingress-from-url/create-ingress-from-url.mjs new file mode 100644 index 0000000000000..c8b078b6d3a9a --- /dev/null +++ b/components/livekit/actions/create-ingress-from-url/create-ingress-from-url.mjs @@ -0,0 +1,87 @@ +import { IngressInput } from "livekit-server-sdk"; +import app from "../../livekit.app.mjs"; + +export default { + key: "livekit-create-ingress-from-url", + name: "Create Ingress From URL", + description: "Create a new ingress from url in LiveKit. [See the documentation](https://docs.livekit.io/home/ingress/overview/#url-input-example).", + version: "0.0.1", + type: "action", + props: { + app, + name: { + type: "string", + label: "Ingress Name", + description: "The name of the ingress", + optional: true, + }, + roomName: { + description: "The name of the room to send media to", + propDefinition: [ + app, + "room", + ], + }, + participantIdentity: { + type: "string", + label: "Participant Identity", + description: "Unique identity of the participant", + }, + participantName: { + type: "string", + label: "Participant Name", + description: "Participant display name", + optional: true, + }, + participantMetadata: { + type: "string", + label: "Participant Metadata", + description: "Metadata to attach to the participant", + optional: true, + }, + bypassTranscoding: { + type: "boolean", + label: "Bypass Transcoding", + description: "Whether to skip transcoding and forward the input media directly. Only supported by WHIP", + optional: true, + }, + enableTranscoding: { + type: "boolean", + label: "Enable Transcoding", + description: "Whether to enable transcoding or forward the input media directly. Transcoding is required for all input types except WHIP. For WHIP, the default is to not transcode.", + optional: true, + }, + url: { + type: "string", + label: "URL", + description: "URL of the media to pull for ingresses of type URL", + }, + }, + async run({ $ }) { + const { + app, + name, + roomName, + participantIdentity, + participantName, + participantMetadata, + bypassTranscoding, + enableTranscoding, + url, + } = this; + + const response = await app.createIngress({ + inputType: IngressInput.URL_INPUT, + name, + roomName, + participantIdentity, + participantName, + participantMetadata, + bypassTranscoding, + enableTranscoding, + url, + }); + $.export("$summary", `Successfully created ingress with ID \`${response.ingressId}\`.`); + return response; + }, +}; diff --git a/components/livekit/actions/create-room/create-room.mjs b/components/livekit/actions/create-room/create-room.mjs new file mode 100644 index 0000000000000..26ee1164f629d --- /dev/null +++ b/components/livekit/actions/create-room/create-room.mjs @@ -0,0 +1,85 @@ +import app from "../../livekit.app.mjs"; + +export default { + key: "livekit-create-room", + name: "Create Room", + description: "Create a new room in LiveKit. [See the documentation](https://docs.livekit.io/home/server/managing-rooms/#create-a-room).", + version: "0.0.1", + type: "action", + props: { + app, + name: { + type: "string", + label: "Room Name", + description: "The name of the room", + }, + emptyTimeout: { + type: "integer", + label: "Empty Timeout", + description: "Number of seconds to keep the room open before any participant joins", + optional: true, + }, + departureTimeout: { + type: "integer", + label: "Departure Timeout", + description: "Number of seconds to keep the room open after the last participant leaves", + optional: true, + }, + maxParticipants: { + type: "integer", + label: "Max Participants", + description: "Limit to the number of participants in a room at a time", + optional: true, + }, + metadata: { + type: "string", + label: "Metadata", + description: "Initial room metadata", + optional: true, + }, + minPlayoutDelay: { + type: "integer", + label: "Min Playout Delay", + description: "Minimum playout delay in milliseconds", + optional: true, + }, + maxPlayoutDelay: { + type: "integer", + label: "Max Playout Delay", + description: "Maximum playout delay in milliseconds", + optional: true, + }, + syncStreams: { + type: "boolean", + label: "Sync Streams", + description: "Improves A/V sync when min_playout_delay set to a value larger than 200ms. It will disables transceiver re-use -- this option is not recommended for rooms with frequent subscription changes", + optional: true, + }, + }, + async run({ $ }) { + const { + app, + name, + emptyTimeout, + departureTimeout, + maxParticipants, + metadata, + minPlayoutDelay, + maxPlayoutDelay, + syncStreams, + } = this; + + const response = await app.createRoom({ + name, + emptyTimeout, + departureTimeout, + maxParticipants, + metadata, + minPlayoutDelay, + maxPlayoutDelay, + syncStreams, + }); + $.export("$summary", `Successfully created room with SID \`${response.sid}\`.`); + return response; + }, +}; diff --git a/components/livekit/actions/delete-room/delete-room.mjs b/components/livekit/actions/delete-room/delete-room.mjs new file mode 100644 index 0000000000000..28c9e6604af30 --- /dev/null +++ b/components/livekit/actions/delete-room/delete-room.mjs @@ -0,0 +1,32 @@ +import app from "../../livekit.app.mjs"; + +export default { + key: "livekit-delete-room", + name: "Delete Room", + description: "Delete a room in LiveKit. [See the documentation](https://docs.livekit.io/home/server/managing-rooms/#delete-a-room)", + version: "0.0.1", + type: "action", + props: { + app, + room: { + propDefinition: [ + app, + "room", + ], + }, + }, + async run({ $ }) { + const { + app, + room, + } = this; + + await app.deleteRoom(room); + + $.export("$summary", "Successfully deleted room."); + + return { + success: true, + }; + }, +}; diff --git a/components/livekit/actions/list-rooms/list-rooms.mjs b/components/livekit/actions/list-rooms/list-rooms.mjs new file mode 100644 index 0000000000000..4a36acc58fe48 --- /dev/null +++ b/components/livekit/actions/list-rooms/list-rooms.mjs @@ -0,0 +1,17 @@ +import app from "../../livekit.app.mjs"; + +export default { + key: "livekit-list-rooms", + name: "List Rooms", + description: "List all rooms with LiveKit. [See the documentation](https://docs.livekit.io/home/server/managing-rooms/#list-rooms).", + version: "0.0.1", + type: "action", + props: { + app, + }, + async run({ $ }) { + const rooms = await this.app.listRooms(); + $.export("$summary", `Successfully listed \`${rooms.length}\` room(s).`); + return rooms; + }, +}; diff --git a/components/livekit/common/constants.mjs b/components/livekit/common/constants.mjs new file mode 100644 index 0000000000000..b777b760f5598 --- /dev/null +++ b/components/livekit/common/constants.mjs @@ -0,0 +1,7 @@ +const HTTPS_PREFIX = "https://"; +const HTTP_PREFIX = "http://"; + +export default { + HTTPS_PREFIX, + HTTP_PREFIX, +}; diff --git a/components/livekit/livekit.app.mjs b/components/livekit/livekit.app.mjs index 7fb5229a0b5b1..d0458075b1ceb 100644 --- a/components/livekit/livekit.app.mjs +++ b/components/livekit/livekit.app.mjs @@ -1,11 +1,62 @@ +import { + RoomServiceClient, + IngressClient, +} from "livekit-server-sdk"; +import constants from "./common/constants.mjs"; + export default { type: "app", app: "livekit", - propDefinitions: {}, + propDefinitions: { + room: { + type: "string", + label: "Room Name", + description: "The name of the room", + async options() { + const rooms = await this.listRooms(); + return rooms.map(({ name }) => name); + }, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + getHost() { + const { project_url: projectUrl } = this.$auth; + + return projectUrl.startsWith(constants.HTTPS_PREFIX) + ? projectUrl + : projectUrl.startsWith(constants.HTTP_PREFIX) + ? projectUrl.replace(constants.HTTP_PREFIX, constants.HTTPS_PREFIX) + : `${constants.HTTPS_PREFIX}${projectUrl}`; + }, + getKeys() { + const { + api_key: apiKey, + secret_key: secretKey, + } = this.$auth; + return [ + apiKey, + secretKey, + ]; + }, + getRoomClient() { + return new RoomServiceClient(this.getHost(), ...this.getKeys()); + }, + getIngressClient() { + return new IngressClient(this.getHost(), ...this.getKeys()); + }, + createRoom(args) { + return this.getRoomClient().createRoom(args); + }, + listRooms(names) { + return this.getRoomClient().listRooms(names); + }, + deleteRoom(room) { + return this.getRoomClient().deleteRoom(room); + }, + createIngress({ + inputType, ...args + } = {}) { + return this.getIngressClient().createIngress(inputType, args); }, }, -}; \ No newline at end of file +}; diff --git a/components/livekit/package.json b/components/livekit/package.json index 66f578a3d3796..8067eda5b705b 100644 --- a/components/livekit/package.json +++ b/components/livekit/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/livekit", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream LiveKit Components", "main": "livekit.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "livekit-server-sdk": "^2.8.1" } -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d845f56367cd7..cf5ea5dbbefca 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5802,7 +5802,10 @@ importers: specifiers: {} components/livekit: - specifiers: {} + specifiers: + livekit-server-sdk: ^2.8.1 + dependencies: + livekit-server-sdk: 2.8.1 components/livesession: specifiers: @@ -16485,6 +16488,10 @@ packages: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} dev: true + /@bufbuild/protobuf/1.10.0: + resolution: {integrity: sha512-QDdVFLoN93Zjg36NoQPZfsVH9tZew7wKDKyV5qRdj8ntT4wQCOradQjRaTdwMhWUYsgKsvCINKKm87FdEk96Ag==} + dev: false + /@bufbuild/protobuf/2.2.2: resolution: {integrity: sha512-UNtPCbrwrenpmrXuRwn9jYpPoweNXj8X5sMvYgsqYyaH8jQ6LfUJSk3dJLnBK+6sfYPrF4iAIo5sd5HQ+tg75A==} dev: false @@ -17697,6 +17704,12 @@ packages: - encoding dev: false + /@livekit/protocol/1.27.1: + resolution: {integrity: sha512-ISEp7uWdV82mtCR1eyHFTzdRZTVbe2+ZztjmjiMPzR/KPrI1Ma/u5kLh87NNuY3Rn8wv1VlEvGHHsFjQ+dKVUw==} + dependencies: + '@bufbuild/protobuf': 1.10.0 + dev: false + /@lob/lob-typescript-sdk/1.3.2: resolution: {integrity: sha512-tRqTg50LsUO4nuw0UHUmj4Ffsus9YsgT13FR1GUhupE2ggpgtB6erTTVYaajMGkS1s21eYuF0L0k3UkrnPSPcw==} engines: {node: '>= 12.0.0'} @@ -23584,6 +23597,16 @@ packages: type-fest: 1.4.0 dev: true + /camelcase-keys/9.1.3: + resolution: {integrity: sha512-Rircqi9ch8AnZscQcsA1C47NFdaO3wukpmIRzYcDOrmvgt78hM/sj5pZhZNec2NM12uk5vTwRHZ4anGcrC4ZTg==} + engines: {node: '>=16'} + dependencies: + camelcase: 8.0.0 + map-obj: 5.0.0 + quick-lru: 6.1.2 + type-fest: 4.15.0 + dev: false + /camelcase/5.3.1: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} @@ -23592,6 +23615,11 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} + /camelcase/8.0.0: + resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} + engines: {node: '>=16'} + dev: false + /caniuse-lite/1.0.30001646: resolution: {integrity: sha512-dRg00gudiBDDTmUhClSdv3hqRfpbOnU28IpI1T6PBTLWa+kOj0681C8uML3PifYfREuBrVjDGhL3adYpBT6spw==} @@ -29430,6 +29458,10 @@ packages: resolution: {integrity: sha512-PRnyOQwWGD3EZnnSpKOOLqQ0RT9chbB8f8AzY4bEHY0I2FCtrcp1ojG0nBgAMn2MtuPpE3wOwIhhW0G7AGzbLw==} dev: false + /jose/5.9.6: + resolution: {integrity: sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==} + dev: false + /js-base64/3.7.2: resolution: {integrity: sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ==} dev: false @@ -30044,6 +30076,15 @@ packages: wrap-ansi: 7.0.0 dev: true + /livekit-server-sdk/2.8.1: + resolution: {integrity: sha512-l8egXU10jPuRJM2Df9Gk/KPEk6tBV0JEGG19cD5QeQtyIMgqULCCd/5yyG2FRvcWRf7pEyZZMXi63zDn7uaKHQ==} + engines: {node: '>=19'} + dependencies: + '@livekit/protocol': 1.27.1 + camelcase-keys: 9.1.3 + jose: 5.9.6 + dev: false + /load-module/4.2.1: resolution: {integrity: sha512-Sbfg6R4LjvyThJpqUoADHMjyoI2+cL4msbCQeZ9kkY/CqP/TT2938eftKm7x4I2gd4/A+DEe6nePkbfWYbXwSw==} engines: {node: '>=12.17'} @@ -30477,6 +30518,11 @@ packages: engines: {node: '>=8'} dev: true + /map-obj/5.0.0: + resolution: {integrity: sha512-2L3MIgJynYrZ3TYMriLDLWocz15okFakV6J12HXvMXDHui2x/zgChzg1u9mFFGbbGWE+GsLpQByt4POb9Or+uA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: false + /map-stream/0.1.0: resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==} dev: true @@ -33568,6 +33614,11 @@ packages: resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} engines: {node: '>=10'} + /quick-lru/6.1.2: + resolution: {integrity: sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==} + engines: {node: '>=12'} + dev: false + /quotemeta/0.0.0: resolution: {integrity: sha512-1XGObUh7RN5b58vKuAsrlfqT+Rc4vmw8N4pP9gFCq1GFlTdV0Ex/D2Ro1Drvrqj++HPi3ig0Np17XPslELeMRA==} dev: false