Skip to content

Commit

Permalink
New Components - spiritme (#12636)
Browse files Browse the repository at this point in the history
* spiritme init

* new components

* pnpm-lock.yaml

* fix pagination
  • Loading branch information
michelle0927 committed Jul 1, 2024
1 parent 0355b13 commit 1f789b1
Show file tree
Hide file tree
Showing 6 changed files with 429 additions and 7 deletions.
160 changes: 160 additions & 0 deletions components/spiritme/actions/generate-video/generate-video.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import spiritme from "../../spiritme.app.mjs";
import { ConfigurationError } from "@pipedream/platform";

export default {
key: "spiritme-generate-video",
name: "Generate Video",
description: "Generates a new video using specific voice and avatar props. [See the documentation](https://api.spiritme.tech/api/swagger/#/videos/videos_create)",
version: "0.0.1",
type: "action",
props: {
spiritme,
name: {
type: "string",
label: "Name",
description: "Name of the video",
},
avatar: {
propDefinition: [
spiritme,
"avatar",
],
},
voice: {
propDefinition: [
spiritme,
"voice",
],
},
text: {
type: "string",
label: "Text",
description: "The text to use for the video. Example: `Hello everyone! I am a virtual avatar from Spiritme.` Use tags `<emotion name=\"emotion_name\"> text </emotion>` to add emotions to the generated video. The list of supported emotions are `neutral`, `semismile`, `smile`, `happiness`, `sadness`, and `surprise`. Either text or audio file is required.",
optional: true,
},
audioFile: {
propDefinition: [
spiritme,
"file",
() => ({
type: [
"audio",
],
}),
],
label: "Audio File",
description: "Identifier of an audio file. Either text or audio file is required.",
},
media: {
propDefinition: [
spiritme,
"file",
() => ({
type: [
"image",
"video",
],
}),
],
label: "Media File",
description: "Identifier of an image or video file. One of avatar or media is required.",
},
viewType: {
type: "string",
label: "View Type",
description: "Content as is or content in circle. Supported only for avatars.",
optional: true,
options: [
"rectangular",
"circular",
],
},
autoEmotionsMarkup: {
type: "boolean",
label: "Auto Emotions Markup",
description: "Add emotions automatically by AI",
optional: true,
},
waitForCompletion: {
type: "boolean",
label: "Wait For Completion",
description: "Set to `true` to poll the API in 3-second intervals until the video is completed",
optional: true,
},
},
async run({ $ }) {
const {
spiritme,
name,
avatar,
voice,
text,
audioFile,
media,
viewType,
autoEmotionsMarkup,
waitForCompletion,
} = this;

if (!avatar && !media) {
throw new ConfigurationError("One of `Avatar` or `Media File` is required");
}

if (!text && !audioFile) {
throw new ConfigurationError("One of `Text` or `Audio File` is required");
}

let response = await spiritme.generateVideo({
$,
data: {
name,
slides: [
{
audio_source: {
text,
voice: voice
? {
id: voice,
}
: undefined,
file: audioFile
? {
id: audioFile,
}
: undefined,
},
layers: [
{
avatar: avatar
? {
id: avatar,
}
: undefined,
media: media
? {
id: media,
}
: undefined,
view_type: viewType,
},
],
},
],
auto_emotions_markup: autoEmotionsMarkup,
},
});

if (waitForCompletion) {
const timer = (ms) => new Promise((res) => setTimeout(res, ms));
while (response.status !== "success") {
response = await spiritme.getVideo({
videoId: response.id,
});
await timer(3000);
}
}

$.export("$summary", `Generated video with ID: ${response.id}`);
return response;
},
};
7 changes: 5 additions & 2 deletions components/spiritme/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@pipedream/spiritme",
"version": "0.0.1",
"version": "0.1.0",
"description": "Pipedream SpiritMe Components",
"main": "spiritme.app.mjs",
"keywords": [
Expand All @@ -11,5 +11,8 @@
"author": "Pipedream <support@pipedream.com> (https://pipedream.com/)",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@pipedream/platform": "^3.0.0"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import spiritme from "../../spiritme.app.mjs";
import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform";
import sampleEmit from "./test-event.mjs";

export default {
key: "spiritme-new-avatar-video-completion",
name: "New Avatar Video Completion",
description: "Emit new event when an avatar video completes rendering.",
version: "0.0.1",
type: "source",
dedupe: "unique",
props: {
spiritme,
db: "$.service.db",
timer: {
type: "$.interface.timer",
default: {
intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL,
},
},
},
hooks: {
async deploy() {
await this.processEvent(25);
},
},
methods: {
_getLastTs() {
return this.db.get("lastTs") || 0;
},
_setLastTs(lastTs) {
this.db.set("lastTs", lastTs);
},
emitEvent(video) {
const meta = this.generateMeta(video);
this.$emit(video, meta);
},
generateMeta(video) {
return {
id: video.id,
summary: `New Video: ${video.name}`,
ts: Date.parse(video.gd),
};
},
async processEvent(max) {
const lastTs = this._getLastTs();
const params = {
limit: 100,
offset: 0,
status: [
"success",
],
};
let total;

const videos = [];
do {
const { results } = await this.spiritme.listVideos({
params,
});
for (const video of results) {
const ts = Date.parse(video.gd);
if (ts >= lastTs && (!max || videos.length < max)) {
videos.push(video);
} else {
break;
}
}
params.offset += params.limit;
total = results?.length;
} while (total === params.limit && (!max || videos.length < max));

if (!videos.length) {
return;
}

this._setLastTs(Date.parse(videos[0].gd));
videos.reverse().forEach((video) => this.emitEvent(video));
},
},
async run() {
await this.processEvent();
},
sampleEmit,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
export default {
"id": 85987,
"cd": "2024-06-28T21:32:14.878982Z",
"gd": "2024-06-28T21:34:37.340485Z",
"name": "Video",
"slides": [
{
"id": 1,
"audio_source": {
"text": "<emotion name=\"happiness\">Pipedream is the best!</emotion>",
"voice": {
"id": 18,
"name": "Kimberly",
"label": "English Kimberly",
"provider": "amazon",
"sex": "female"
},
"file": null
},
"layers": [
{
"id": 1,
"avatar": {
"id": 9889,
"name": "Elizabeth",
"preview": "https://cdn.spiritme.tech/media/avatars/previews/ec724f6e58a22d3ec9dd81fe825f3eb1ea2a99b6.png",
"frame_width": 1073,
"frame_height": 1177
},
"media": null,
"x": 0.5,
"y": 1,
"scale": 1,
"crop": null,
"view_type": "rectangular"
}
],
"background": null
}
],
"webhook": null,
"resolution": {
"width": 1920,
"height": 1080
},
"auto_emotions_markup": true,
"enable_subtitles": false,
"status": "success",
"result": "https://cdn.spiritme.tech/media/videos/3fa94ae367237676442cde44feacaf5c24875966.mp4",
"thumbnail": "https://cdn.spiritme.tech/media/videos/65dd50bc2b996b3457fcb4d98ce5c270bed54a6d.jpeg"
}
Loading

0 comments on commit 1f789b1

Please sign in to comment.