From b1e1ea80cd49a2077a8e34999d503b80e207aeda Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Mon, 20 May 2024 16:43:13 -0300 Subject: [PATCH 1/8] hippo_video init --- .../actions/create-contact/create-contact.mjs | 29 +++++ .../send-personalization-request.mjs | 26 ++++ .../actions/upload-video/upload-video.mjs | 26 ++++ components/hippo_video/hippo_video.app.mjs | 111 +++++++++++++++++- components/hippo_video/package.json | 2 +- .../hippo_video/sources/new-lead/new-lead.mjs | 63 ++++++++++ .../new-video-render-completion.mjs | 64 ++++++++++ .../new-video-watched/new-video-watched.mjs | 61 ++++++++++ 8 files changed, 376 insertions(+), 6 deletions(-) create mode 100644 components/hippo_video/actions/create-contact/create-contact.mjs create mode 100644 components/hippo_video/actions/send-personalization-request/send-personalization-request.mjs create mode 100644 components/hippo_video/actions/upload-video/upload-video.mjs create mode 100644 components/hippo_video/sources/new-lead/new-lead.mjs create mode 100644 components/hippo_video/sources/new-video-render-completion/new-video-render-completion.mjs create mode 100644 components/hippo_video/sources/new-video-watched/new-video-watched.mjs diff --git a/components/hippo_video/actions/create-contact/create-contact.mjs b/components/hippo_video/actions/create-contact/create-contact.mjs new file mode 100644 index 0000000000000..532762ef5a514 --- /dev/null +++ b/components/hippo_video/actions/create-contact/create-contact.mjs @@ -0,0 +1,29 @@ +import hippoVideo from "../../hippo_video.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "hippo_video-create-contact", + name: "Create Contact", + description: "Creates a new contact in Hippo Video. Requires 'contact_name' and 'contact_email'. Optional prop: 'contact_phone_number'.", + version: "0.0.1", + type: "action", + props: { + hippoVideo, + contactName: hippoVideo.propDefinitions.contactName, + contactEmail: hippoVideo.propDefinitions.contactEmail, + contactPhoneNumber: { + ...hippoVideo.propDefinitions.contactPhoneNumber, + optional: true, + }, + }, + async run({ $ }) { + const response = await this.hippoVideo.createContact({ + contactName: this.contactName, + contactEmail: this.contactEmail, + contactPhoneNumber: this.contactPhoneNumber || undefined, + }); + + $.export("$summary", `Successfully created contact ${this.contactName}`); + return response; + }, +}; diff --git a/components/hippo_video/actions/send-personalization-request/send-personalization-request.mjs b/components/hippo_video/actions/send-personalization-request/send-personalization-request.mjs new file mode 100644 index 0000000000000..d8ab15e7183a1 --- /dev/null +++ b/components/hippo_video/actions/send-personalization-request/send-personalization-request.mjs @@ -0,0 +1,26 @@ +import hippoVideo from "../../hippo_video.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "hippo_video-send-personalization-request", + name: "Send Personalization Request", + description: "Sends a personalization request for a specified video.", + version: "0.0.1", + type: "action", + props: { + hippoVideo, + videoId: { + propDefinition: [ + hippoVideo, + "videoId", + ], + }, + }, + async run({ $ }) { + const response = await this.hippoVideo.personalizeVideo({ + videoId: this.videoId, + }); + $.export("$summary", `Successfully sent personalization request for video ID: ${this.videoId}`); + return response; + }, +}; diff --git a/components/hippo_video/actions/upload-video/upload-video.mjs b/components/hippo_video/actions/upload-video/upload-video.mjs new file mode 100644 index 0000000000000..d6d3d7de9f152 --- /dev/null +++ b/components/hippo_video/actions/upload-video/upload-video.mjs @@ -0,0 +1,26 @@ +import hippoVideo from "../../hippo_video.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "hippo_video-upload-video", + name: "Upload Video", + description: "Uploads a video from a given URL. [See the documentation](https://api.hippovideo.io/v1/videos/upload)", + version: "0.0.1", + type: "action", + props: { + hippoVideo, + videoUrl: { + propDefinition: [ + hippoVideo, + "videoUrl", + ], + }, + }, + async run({ $ }) { + const response = await this.hippoVideo.uploadVideo({ + videoUrl: this.videoUrl, + }); + $.export("$summary", `Video uploaded successfully with URL: ${this.videoUrl}`); + return response; + }, +}; diff --git a/components/hippo_video/hippo_video.app.mjs b/components/hippo_video/hippo_video.app.mjs index 18c2c58a0e6cd..f15370342635f 100644 --- a/components/hippo_video/hippo_video.app.mjs +++ b/components/hippo_video/hippo_video.app.mjs @@ -1,11 +1,112 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "hippo_video", - propDefinitions: {}, + propDefinitions: { + videoId: { + type: "string", + label: "Video ID", + description: "The ID of the video to be personalized or to track analytics for.", + }, + videoUrl: { + type: "string", + label: "Video URL", + description: "The URL pointing to the video to be uploaded.", + }, + contactName: { + type: "string", + label: "Contact Name", + description: "The name of the contact to create.", + }, + contactEmail: { + type: "string", + label: "Contact Email", + description: "The email of the contact to create.", + }, + contactPhoneNumber: { + type: "string", + label: "Contact Phone Number", + description: "The phone number of the contact to create (optional).", + optional: true, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return "https://api.hippovideo.io/v1"; + }, + async _makeRequest(opts = {}) { + const { + $ = this, + method = "GET", + path, + data, + params, + headers, + ...otherOpts + } = opts; + return axios($, { + method, + url: `${this._baseUrl()}${path}`, + data, + params, + headers: { + Authorization: `Bearer ${this.$auth.oauth_access_token}`, + ...headers, + }, + ...otherOpts, + }); + }, + async createContact({ + contactName, contactEmail, contactPhoneNumber, + }) { + return this._makeRequest({ + method: "POST", + path: "/contacts", + headers: { + "Content-Type": "application/json", + }, + data: { + name: contactName, + email: contactEmail, + phone: contactPhoneNumber, + }, + }); + }, + async uploadVideo({ videoUrl }) { + return this._makeRequest({ + method: "POST", + path: "/videos/upload", + headers: { + "Content-Type": "application/json", + }, + data: { + url: videoUrl, + }, + }); + }, + async personalizeVideo({ videoId }) { + return this._makeRequest({ + method: "POST", + path: `/videos/${videoId}/personalize`, + }); + }, + async getVideoAnalytics({ videoId }) { + return this._makeRequest({ + path: `/videos/${videoId}/analytics`, + }); + }, + async emitNewLeadEvent(videoId) { + // Method to handle new lead event for a specific video + // This method needs to be implemented based on the event system in use + }, + async emitNewPersonalizedVideoEvent() { + // Method to handle new personalized video event + // This method needs to be implemented based on the event system in use + }, + async emitVideoWatchEvent(videoId) { + // Method to handle video watch event for a specific video + // This method needs to be implemented based on the event system in use }, }, -}; \ No newline at end of file +}; diff --git a/components/hippo_video/package.json b/components/hippo_video/package.json index 017dfdfae0fdb..f227f155ae25b 100644 --- a/components/hippo_video/package.json +++ b/components/hippo_video/package.json @@ -12,4 +12,4 @@ "publishConfig": { "access": "public" } -} \ No newline at end of file +} diff --git a/components/hippo_video/sources/new-lead/new-lead.mjs b/components/hippo_video/sources/new-lead/new-lead.mjs new file mode 100644 index 0000000000000..b8d0d157eeba0 --- /dev/null +++ b/components/hippo_video/sources/new-lead/new-lead.mjs @@ -0,0 +1,63 @@ +import { axios } from "@pipedream/platform"; +import hippoVideo from "../../hippo_video.app.mjs"; + +export default { + key: "hippo_video-new-lead", + name: "New Lead from Video", + description: "Emits an event when a video generates a new lead. [See the documentation](https://help.hippovideo.io/support/solutions/articles/19000095984-video-reports-api)", + version: "0.0.1", + type: "source", + dedupe: "unique", + props: { + hippoVideo, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: 60 * 15, // 15 minutes + }, + }, + videoId: { + propDefinition: [ + hippoVideo, + "videoId", + ], + }, + }, + methods: { + ...hippoVideo.methods, + async fetchLeads(videoId) { + const analyticsData = await this.hippoVideo.getVideoAnalytics({ + videoId, + }); + // Assuming analyticsData contains leads information + return analyticsData.leads || []; + }, + }, + async run() { + const videoId = this.videoId; + const leads = await this.fetchLeads(videoId); + + if (!leads || leads.length === 0) { + console.log("No new leads found."); + return; + } + + leads.forEach((lead) => { + const emitId = lead.email + ? `${videoId}-${lead.email}-${lead.last_viewed_time}` + : `${videoId}-${Date.now()}`; + const summary = lead.name + ? `New lead ${lead.name} from video ${videoId}` + : `New lead from video ${videoId}`; + + this.$emit(lead, { + id: emitId, + summary, + ts: lead.last_viewed_time + ? Date.parse(lead.last_viewed_time) + : new Date().getTime(), + }); + }); + }, +}; diff --git a/components/hippo_video/sources/new-video-render-completion/new-video-render-completion.mjs b/components/hippo_video/sources/new-video-render-completion/new-video-render-completion.mjs new file mode 100644 index 0000000000000..7daeef05e7e92 --- /dev/null +++ b/components/hippo_video/sources/new-video-render-completion/new-video-render-completion.mjs @@ -0,0 +1,64 @@ +import { axios } from "@pipedream/platform"; +import hippoVideo from "../../hippo_video.app.mjs"; + +export default { + key: "hippo_video-new-video-render-completion", + name: "New Video Render Completion", + description: "Emits an event when a personalized video is generated.", + version: "0.0.1", + type: "source", + dedupe: "unique", + props: { + hippoVideo, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: 60 * 15, // 15 minutes + }, + }, + }, + hooks: { + async deploy() { + // Fetch and emit events for the most recent videos up to the last 50 + const videos = await this.hippoVideo.getRecentlyRenderedVideos(); + videos.slice(0, 50).forEach((video) => { + if (video.status === "rendered") { + this.$emit(video, { + id: video.id, + summary: `New video rendered: ${video.title}`, + ts: Date.parse(video.createdAt), + }); + } + }); + }, + }, + methods: { + ...hippoVideo.methods, + async getRecentlyRenderedVideos() { + const response = await this.hippoVideo._makeRequest({ + path: "/videos", + params: { + status: "rendered", + }, + }); + return response.data.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)); + }, + }, + async run() { + const videos = await this.getRecentlyRenderedVideos(); + videos.forEach((video) => { + if (video.status === "rendered") { + const latestVideoId = this.db.get("latestVideoId") || 0; + if (video.id > latestVideoId) { + this.$emit(video, { + id: video.id, + summary: `New video rendered: ${video.title}`, + ts: Date.parse(video.createdAt), + }); + this.db.set("latestVideoId", video.id); + } + } + }); + }, +}; diff --git a/components/hippo_video/sources/new-video-watched/new-video-watched.mjs b/components/hippo_video/sources/new-video-watched/new-video-watched.mjs new file mode 100644 index 0000000000000..ea870a28e50bf --- /dev/null +++ b/components/hippo_video/sources/new-video-watched/new-video-watched.mjs @@ -0,0 +1,61 @@ +import { + axios, DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, +} from "@pipedream/platform"; +import hippoVideo from "../../hippo_video.app.mjs"; + +export default { + key: "hippo_video-new-video-watched", + name: "New Video Watched", + description: "Emits an event each time a visitor watches a video.", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + hippoVideo, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + videoId: { + propDefinition: [ + hippoVideo, + "videoId", + ], + }, + }, + hooks: { + async deploy() { + // Emit events for the first time during deployment + await this.fetchAndEmitEvents(); + }, + async activate() { + // Placeholder for hook logic on activation + }, + async deactivate() { + // Placeholder for hook logic on deactivation + }, + }, + methods: { + async fetchAndEmitEvents() { + const videoId = this.videoId; + const analytics = await this.hippoVideo.getVideoAnalytics({ + videoId, + }); + + analytics.viewer_profile.forEach((profile) => { + const event = { + id: `${profile.video_id}-${profile.email}-${profile.last_viewed_time}`, + summary: `Video ${profile.video_id} watched by ${profile.email}`, + ts: new Date(profile.last_viewed_time).getTime(), + }; + this.$emit(profile, event); + }); + }, + }, + async run() { + await this.fetchAndEmitEvents(); + }, +}; From 3885962ad9206ec3ac7effd918c3e002517f818b Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Tue, 21 May 2024 17:35:11 -0300 Subject: [PATCH 2/8] [Components] hippo_video #10752 Sources: - New Event Triggered (Instant) Actions - Send Personalization Request - Upload Video - Create Contact --- .../actions/create-contact/create-contact.mjs | 62 ++++++-- .../send-personalization-request.mjs | 28 +++- .../actions/upload-video/upload-video.mjs | 29 ++-- components/hippo_video/common/constants.mjs | 26 ++++ components/hippo_video/common/utils.mjs | 31 ++++ components/hippo_video/hippo_video.app.mjs | 141 ++++++++---------- components/hippo_video/package.json | 6 +- .../hippo_video/sources/new-lead/new-lead.mjs | 63 -------- .../new-triggered-event-instant.mjs | 67 +++++++++ .../new-video-render-completion.mjs | 64 -------- .../new-video-watched/new-video-watched.mjs | 61 -------- 11 files changed, 291 insertions(+), 287 deletions(-) create mode 100644 components/hippo_video/common/constants.mjs create mode 100644 components/hippo_video/common/utils.mjs delete mode 100644 components/hippo_video/sources/new-lead/new-lead.mjs create mode 100644 components/hippo_video/sources/new-triggered-event-instant/new-triggered-event-instant.mjs delete mode 100644 components/hippo_video/sources/new-video-render-completion/new-video-render-completion.mjs delete mode 100644 components/hippo_video/sources/new-video-watched/new-video-watched.mjs diff --git a/components/hippo_video/actions/create-contact/create-contact.mjs b/components/hippo_video/actions/create-contact/create-contact.mjs index 532762ef5a514..aac23f2470139 100644 --- a/components/hippo_video/actions/create-contact/create-contact.mjs +++ b/components/hippo_video/actions/create-contact/create-contact.mjs @@ -1,29 +1,71 @@ +import { ConfigurationError } from "@pipedream/platform"; +import { parseObject } from "../../common/utils.mjs"; import hippoVideo from "../../hippo_video.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "hippo_video-create-contact", name: "Create Contact", - description: "Creates a new contact in Hippo Video. Requires 'contact_name' and 'contact_email'. Optional prop: 'contact_phone_number'.", + description: "Creates a new contact in Hippo Video. [See the documentation](https://documenter.getpostman.com/view/5278433/Tz5naxpW#a4d73ffe-a2b6-4d68-a1ee-9fbc1417a955)", version: "0.0.1", type: "action", props: { hippoVideo, - contactName: hippoVideo.propDefinitions.contactName, - contactEmail: hippoVideo.propDefinitions.contactEmail, - contactPhoneNumber: { - ...hippoVideo.propDefinitions.contactPhoneNumber, + contactEmail: { + type: "string", + label: "Contact Email", + description: "Email Address of the Contact/Lead/Prospect.", + }, + firstName: { + type: "string", + label: "First Name", + description: "First Name of the Contact/Lead/Prospect.", + }, + lastName: { + type: "string", + label: "Last Name", + description: "Last Name of the Contact/Lead/Prospect.", + }, + companyName: { + type: "string", + label: "Company name", + description: "Company Name of the Contact/Lead/Prospect.", + }, + notes: { + type: "string", + label: "Notes", + description: "Notes for the lead/prospect/contact.", + }, + context: { + type: "string", + label: "Context", + description: "If sales, will be added as a prospect. If empty, will be added as people.", + optional: true, + }, + listIds: { + propDefinition: [ + hippoVideo, + "listIds", + ], optional: true, }, }, async run({ $ }) { const response = await this.hippoVideo.createContact({ - contactName: this.contactName, - contactEmail: this.contactEmail, - contactPhoneNumber: this.contactPhoneNumber || undefined, + $, + data: { + contact_email: this.contactEmail, + first_name: this.firstName, + last_name: this.lastName, + company_name: this.companyName, + notes: this.notes, + context: this.context, + list_ids: parseObject(this.listIds).join(), + }, }); - $.export("$summary", `Successfully created contact ${this.contactName}`); + if (!response.status) throw new ConfigurationError(response.message); + + $.export("$summary", `Successfully created contact with Id: ${response.contact_id}`); return response; }, }; diff --git a/components/hippo_video/actions/send-personalization-request/send-personalization-request.mjs b/components/hippo_video/actions/send-personalization-request/send-personalization-request.mjs index d8ab15e7183a1..4688fbd3a352b 100644 --- a/components/hippo_video/actions/send-personalization-request/send-personalization-request.mjs +++ b/components/hippo_video/actions/send-personalization-request/send-personalization-request.mjs @@ -1,10 +1,13 @@ +import { ConfigurationError } from "@pipedream/platform"; +import FormData from "form-data"; +import fs from "fs"; +import { checkTmp } from "../../common/utils.mjs"; import hippoVideo from "../../hippo_video.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "hippo_video-send-personalization-request", name: "Send Personalization Request", - description: "Sends a personalization request for a specified video.", + description: "Sends a personalization request for a specified video. [See the documentation](https://help.hippovideo.io/support/solutions/articles/19000099793-bulk-video-personalization-and-tracking-api)", version: "0.0.1", type: "action", props: { @@ -15,12 +18,29 @@ export default { "videoId", ], }, + file: { + type: "string", + label: "File", + description: "csv, xls, and xlsx file saved to the `/tmp` directory (e.g. `/tmp/example.jpg`). [See the documentation](https://pipedream.com/docs/workflows/steps/code/nodejs/working-with-files/#the-tmp-directory) which has data for video personalization.", + }, }, async run({ $ }) { + const formData = new FormData(); + const file = fs.createReadStream(checkTmp(this.file)); + + formData.append("file", file); + formData.append("video_id", this.videoId); + const response = await this.hippoVideo.personalizeVideo({ - videoId: this.videoId, + $, + data: formData, + headers: formData.getHeaders(), + maxBodyLength: Infinity, }); - $.export("$summary", `Successfully sent personalization request for video ID: ${this.videoId}`); + + if (!response.code != 200) throw new ConfigurationError(response.message || response.type); + + $.export("$summary", `Successfully sent personalization request for video Id: ${this.videoId}`); return response; }, }; diff --git a/components/hippo_video/actions/upload-video/upload-video.mjs b/components/hippo_video/actions/upload-video/upload-video.mjs index d6d3d7de9f152..ffec45f81a555 100644 --- a/components/hippo_video/actions/upload-video/upload-video.mjs +++ b/components/hippo_video/actions/upload-video/upload-video.mjs @@ -1,26 +1,37 @@ +import { ConfigurationError } from "@pipedream/platform"; import hippoVideo from "../../hippo_video.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "hippo_video-upload-video", name: "Upload Video", - description: "Uploads a video from a given URL. [See the documentation](https://api.hippovideo.io/v1/videos/upload)", + description: "Uploads a video from a given URL. [See the documentation](https://help.hippovideo.io/support/solutions/articles/19000100703-import-api)", version: "0.0.1", type: "action", props: { hippoVideo, - videoUrl: { - propDefinition: [ - hippoVideo, - "videoUrl", - ], + title: { + type: "string", + label: "Title", + description: "Title of the video.", + }, + url: { + type: "string", + label: "Video URL", + description: "Public URL of the videos to be downloaded and uploaded to HippoVideo.", }, }, async run({ $ }) { const response = await this.hippoVideo.uploadVideo({ - videoUrl: this.videoUrl, + $, + data: { + title: this.title, + url: this.url, + }, }); - $.export("$summary", `Video uploaded successfully with URL: ${this.videoUrl}`); + + if (!response.status) throw new ConfigurationError(response.message); + + $.export("$summary", `Video uploaded successfully with Id: ${response.video_id}`); return response; }, }; diff --git a/components/hippo_video/common/constants.mjs b/components/hippo_video/common/constants.mjs new file mode 100644 index 0000000000000..a3fed4e052594 --- /dev/null +++ b/components/hippo_video/common/constants.mjs @@ -0,0 +1,26 @@ +export const EVENT_OPTIONS = [ + { + value: "3", + label: "Viewing Sessions", + }, + { + value: "1", + label: "Video Created", + }, + { + value: "4", + label: "CTA Lead Genaration", + }, + { + value: "5", + label: "CTA Annotations", + }, + { + value: "6", + label: "CT", + }, + { + value: "18", + label: "Video Transcode", + }, +]; diff --git a/components/hippo_video/common/utils.mjs b/components/hippo_video/common/utils.mjs new file mode 100644 index 0000000000000..d1e7ed1a22d98 --- /dev/null +++ b/components/hippo_video/common/utils.mjs @@ -0,0 +1,31 @@ +export const parseObject = (obj) => { + if (!obj) return undefined; + + if (Array.isArray(obj)) { + return obj.map((item) => { + if (typeof item === "string") { + try { + return JSON.parse(item); + } catch (e) { + return item; + } + } + return item; + }); + } + if (typeof obj === "string") { + try { + return JSON.parse(obj); + } catch (e) { + return obj; + } + } + return obj; +}; + +export const checkTmp = (filename) => { + if (!filename.startsWith("/tmp")) { + return `/tmp/${filename}`; + } + return filename; +}; diff --git a/components/hippo_video/hippo_video.app.mjs b/components/hippo_video/hippo_video.app.mjs index f15370342635f..e9e57da9a43a8 100644 --- a/components/hippo_video/hippo_video.app.mjs +++ b/components/hippo_video/hippo_video.app.mjs @@ -4,109 +4,100 @@ export default { type: "app", app: "hippo_video", propDefinitions: { + listIds: { + type: "string[]", + label: "List Ids", + description: "A list of lists you want to add the prospect.", + async options({ page }) { + const { contacts_list: data } = await this.listLists({ + params: { + page, + }, + }); + + return data.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, + }, videoId: { type: "string", label: "Video ID", - description: "The ID of the video to be personalized or to track analytics for.", - }, - videoUrl: { - type: "string", - label: "Video URL", - description: "The URL pointing to the video to be uploaded.", - }, - contactName: { - type: "string", - label: "Contact Name", - description: "The name of the contact to create.", - }, - contactEmail: { - type: "string", - label: "Contact Email", - description: "The email of the contact to create.", - }, - contactPhoneNumber: { - type: "string", - label: "Contact Phone Number", - description: "The phone number of the contact to create (optional).", - optional: true, + description: "The ID of the video to be personalized.", + async options({ page }) { + const { videos } = await this.listVideos({ + params: { + page: page + 1, + }, + }); + + return videos.map(({ + id: value, title: label, + }) => ({ + label, + value, + })); + }, }, }, methods: { _baseUrl() { - return "https://api.hippovideo.io/v1"; + return "https://www.hippovideo.io"; }, - async _makeRequest(opts = {}) { - const { - $ = this, - method = "GET", - path, - data, - params, - headers, - ...otherOpts - } = opts; + _params(params = {}) { + return { + ...params, + email: `${this.$auth.email}`, + api_key: `${this.$auth.api_key}`, + }; + }, + _makeRequest({ + $ = this, path, params, ...opts + }) { return axios($, { - method, url: `${this._baseUrl()}${path}`, - data, - params, + params: this._params(params), headers: { - Authorization: `Bearer ${this.$auth.oauth_access_token}`, - ...headers, + "Accept": "*/*", }, - ...otherOpts, + ...opts, }); }, - async createContact({ - contactName, contactEmail, contactPhoneNumber, - }) { + createContact(opts = {}) { return this._makeRequest({ method: "POST", path: "/contacts", - headers: { - "Content-Type": "application/json", - }, - data: { - name: contactName, - email: contactEmail, - phone: contactPhoneNumber, - }, + ...opts, }); }, - async uploadVideo({ videoUrl }) { + listLists(opts = {}) { return this._makeRequest({ - method: "POST", - path: "/videos/upload", - headers: { - "Content-Type": "application/json", - }, - data: { - url: videoUrl, - }, + path: "/contacts/names", + ...opts, }); }, - async personalizeVideo({ videoId }) { + listVideos(opts = {}) { return this._makeRequest({ - method: "POST", - path: `/videos/${videoId}/personalize`, + path: "/api/v1/me/videos/list", + ...opts, }); }, - async getVideoAnalytics({ videoId }) { + uploadVideo(opts = {}) { return this._makeRequest({ - path: `/videos/${videoId}/analytics`, + method: "POST", + path: "/api/v1/me/video/import", + ...opts, }); }, - async emitNewLeadEvent(videoId) { - // Method to handle new lead event for a specific video - // This method needs to be implemented based on the event system in use - }, - async emitNewPersonalizedVideoEvent() { - // Method to handle new personalized video event - // This method needs to be implemented based on the event system in use - }, - async emitVideoWatchEvent(videoId) { - // Method to handle video watch event for a specific video - // This method needs to be implemented based on the event system in use + updateWebhook(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/webhook", + ...opts, + }); }, }, }; diff --git a/components/hippo_video/package.json b/components/hippo_video/package.json index f227f155ae25b..2bc31f114dcb4 100644 --- a/components/hippo_video/package.json +++ b/components/hippo_video/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/hippo_video", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Hippo Video Components", "main": "hippo_video.app.mjs", "keywords": [ @@ -11,5 +11,9 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.6.5" } } + diff --git a/components/hippo_video/sources/new-lead/new-lead.mjs b/components/hippo_video/sources/new-lead/new-lead.mjs deleted file mode 100644 index b8d0d157eeba0..0000000000000 --- a/components/hippo_video/sources/new-lead/new-lead.mjs +++ /dev/null @@ -1,63 +0,0 @@ -import { axios } from "@pipedream/platform"; -import hippoVideo from "../../hippo_video.app.mjs"; - -export default { - key: "hippo_video-new-lead", - name: "New Lead from Video", - description: "Emits an event when a video generates a new lead. [See the documentation](https://help.hippovideo.io/support/solutions/articles/19000095984-video-reports-api)", - version: "0.0.1", - type: "source", - dedupe: "unique", - props: { - hippoVideo, - db: "$.service.db", - timer: { - type: "$.interface.timer", - default: { - intervalSeconds: 60 * 15, // 15 minutes - }, - }, - videoId: { - propDefinition: [ - hippoVideo, - "videoId", - ], - }, - }, - methods: { - ...hippoVideo.methods, - async fetchLeads(videoId) { - const analyticsData = await this.hippoVideo.getVideoAnalytics({ - videoId, - }); - // Assuming analyticsData contains leads information - return analyticsData.leads || []; - }, - }, - async run() { - const videoId = this.videoId; - const leads = await this.fetchLeads(videoId); - - if (!leads || leads.length === 0) { - console.log("No new leads found."); - return; - } - - leads.forEach((lead) => { - const emitId = lead.email - ? `${videoId}-${lead.email}-${lead.last_viewed_time}` - : `${videoId}-${Date.now()}`; - const summary = lead.name - ? `New lead ${lead.name} from video ${videoId}` - : `New lead from video ${videoId}`; - - this.$emit(lead, { - id: emitId, - summary, - ts: lead.last_viewed_time - ? Date.parse(lead.last_viewed_time) - : new Date().getTime(), - }); - }); - }, -}; diff --git a/components/hippo_video/sources/new-triggered-event-instant/new-triggered-event-instant.mjs b/components/hippo_video/sources/new-triggered-event-instant/new-triggered-event-instant.mjs new file mode 100644 index 0000000000000..35c5258e13690 --- /dev/null +++ b/components/hippo_video/sources/new-triggered-event-instant/new-triggered-event-instant.mjs @@ -0,0 +1,67 @@ +import { v4 as uuidv4 } from "uuid"; +import { EVENT_OPTIONS } from "../../common/constants.mjs"; +import { parseObject } from "../../common/utils.mjs"; +import hippoVideo from "../../hippo_video.app.mjs"; + +export default { + key: "hippo_video-new-triggered-event-instant", + name: "New Event Triggered (Instant)", + description: "Emit new event when any of the selected events are triggered.", + version: "0.0.1", + type: "source", + dedupe: "unique", + props: { + hippoVideo, + db: "$.service.db", + http: { + type: "$.interface.http", + customResponse: true, + }, + alert: { + type: "alert", + alertType: "warning", + content: "Please note that you can have only one webhook in your Hippo Video account, any change here will overwrite the current webhook configuration.", + }, + eventIds: { + type: "string[]", + label: "Event Ids", + description: "The Id of the events you want to be triggered.", + options: EVENT_OPTIONS, + }, + }, + methods: { + removeHook() { + this.hippoVideo.updateWebhook({ + data: { + url: this.http.endpoint, + secret_key: uuidv4(), + }, + }); + }, + }, + hooks: { + async activate() { + await this.removeHook(); + await this.hippoVideo.updateWebhook({ + data: { + events: parseObject(this.eventIds), + url: this.http.endpoint, + secret_key: uuidv4(), + }, + }); + }, + async deactivate() { + await this.removeHook(); + }, + }, + async run(event) { + const { body } = event; + const ts = Date.parse(body.eventTimestamp || new Date()); + + this.$emit(body, { + id: body.hook?.uuid || body.video_token, + summary: `New event with event name: ${body.eventName || body.type} successfully triggered!`, + ts: ts, + }); + }, +}; diff --git a/components/hippo_video/sources/new-video-render-completion/new-video-render-completion.mjs b/components/hippo_video/sources/new-video-render-completion/new-video-render-completion.mjs deleted file mode 100644 index 7daeef05e7e92..0000000000000 --- a/components/hippo_video/sources/new-video-render-completion/new-video-render-completion.mjs +++ /dev/null @@ -1,64 +0,0 @@ -import { axios } from "@pipedream/platform"; -import hippoVideo from "../../hippo_video.app.mjs"; - -export default { - key: "hippo_video-new-video-render-completion", - name: "New Video Render Completion", - description: "Emits an event when a personalized video is generated.", - version: "0.0.1", - type: "source", - dedupe: "unique", - props: { - hippoVideo, - db: "$.service.db", - timer: { - type: "$.interface.timer", - default: { - intervalSeconds: 60 * 15, // 15 minutes - }, - }, - }, - hooks: { - async deploy() { - // Fetch and emit events for the most recent videos up to the last 50 - const videos = await this.hippoVideo.getRecentlyRenderedVideos(); - videos.slice(0, 50).forEach((video) => { - if (video.status === "rendered") { - this.$emit(video, { - id: video.id, - summary: `New video rendered: ${video.title}`, - ts: Date.parse(video.createdAt), - }); - } - }); - }, - }, - methods: { - ...hippoVideo.methods, - async getRecentlyRenderedVideos() { - const response = await this.hippoVideo._makeRequest({ - path: "/videos", - params: { - status: "rendered", - }, - }); - return response.data.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)); - }, - }, - async run() { - const videos = await this.getRecentlyRenderedVideos(); - videos.forEach((video) => { - if (video.status === "rendered") { - const latestVideoId = this.db.get("latestVideoId") || 0; - if (video.id > latestVideoId) { - this.$emit(video, { - id: video.id, - summary: `New video rendered: ${video.title}`, - ts: Date.parse(video.createdAt), - }); - this.db.set("latestVideoId", video.id); - } - } - }); - }, -}; diff --git a/components/hippo_video/sources/new-video-watched/new-video-watched.mjs b/components/hippo_video/sources/new-video-watched/new-video-watched.mjs deleted file mode 100644 index ea870a28e50bf..0000000000000 --- a/components/hippo_video/sources/new-video-watched/new-video-watched.mjs +++ /dev/null @@ -1,61 +0,0 @@ -import { - axios, DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, -} from "@pipedream/platform"; -import hippoVideo from "../../hippo_video.app.mjs"; - -export default { - key: "hippo_video-new-video-watched", - name: "New Video Watched", - description: "Emits an event each time a visitor watches a video.", - version: "0.0.{{ts}}", - type: "source", - dedupe: "unique", - props: { - hippoVideo, - db: "$.service.db", - timer: { - type: "$.interface.timer", - default: { - intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, - }, - }, - videoId: { - propDefinition: [ - hippoVideo, - "videoId", - ], - }, - }, - hooks: { - async deploy() { - // Emit events for the first time during deployment - await this.fetchAndEmitEvents(); - }, - async activate() { - // Placeholder for hook logic on activation - }, - async deactivate() { - // Placeholder for hook logic on deactivation - }, - }, - methods: { - async fetchAndEmitEvents() { - const videoId = this.videoId; - const analytics = await this.hippoVideo.getVideoAnalytics({ - videoId, - }); - - analytics.viewer_profile.forEach((profile) => { - const event = { - id: `${profile.video_id}-${profile.email}-${profile.last_viewed_time}`, - summary: `Video ${profile.video_id} watched by ${profile.email}`, - ts: new Date(profile.last_viewed_time).getTime(), - }; - this.$emit(profile, event); - }); - }, - }, - async run() { - await this.fetchAndEmitEvents(); - }, -}; From 84bf6ada2110a544086846c88e529e3a9175b975 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Tue, 21 May 2024 17:36:39 -0300 Subject: [PATCH 3/8] pnpm update --- pnpm-lock.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 96957d7d10faf..2087c49976c80 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3878,7 +3878,10 @@ importers: '@pipedream/platform': 1.6.2 components/hippo_video: - specifiers: {} + specifiers: + '@pipedream/platform': ^1.6.5 + dependencies: + '@pipedream/platform': 1.6.5 components/hive: specifiers: From c285ccd9d5609b2f1ade9f3ca001b5b9a952c6c5 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Tue, 21 May 2024 17:57:29 -0300 Subject: [PATCH 4/8] add form-data --- components/hippo_video/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/hippo_video/package.json b/components/hippo_video/package.json index 2bc31f114dcb4..b1fa650fe4391 100644 --- a/components/hippo_video/package.json +++ b/components/hippo_video/package.json @@ -13,7 +13,7 @@ "access": "public" }, "dependencies": { - "@pipedream/platform": "^1.6.5" + "@pipedream/platform": "^1.6.5", + "form-data": "^4.0.0" } } - From e35b2245356342771b8c96562df307f35d1afdd3 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Tue, 21 May 2024 17:58:02 -0300 Subject: [PATCH 5/8] pnpm update --- pnpm-lock.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2087c49976c80..bc11dfd581537 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3880,8 +3880,10 @@ importers: components/hippo_video: specifiers: '@pipedream/platform': ^1.6.5 + form-data: ^4.0.0 dependencies: '@pipedream/platform': 1.6.5 + form-data: 4.0.0 components/hive: specifiers: From 9851c361c24164728d335b2c31f2d5f7ed6a10a3 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Thu, 23 May 2024 11:00:25 -0300 Subject: [PATCH 6/8] some adjusts --- .../hippo_video/actions/create-contact/create-contact.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/hippo_video/actions/create-contact/create-contact.mjs b/components/hippo_video/actions/create-contact/create-contact.mjs index aac23f2470139..b2b6694500d2b 100644 --- a/components/hippo_video/actions/create-contact/create-contact.mjs +++ b/components/hippo_video/actions/create-contact/create-contact.mjs @@ -59,7 +59,7 @@ export default { company_name: this.companyName, notes: this.notes, context: this.context, - list_ids: parseObject(this.listIds).join(), + list_ids: parseObject(this.listIds)?.join(), }, }); From 688dd8b2ca90fbb2e4d889ea44e7239eec44606f Mon Sep 17 00:00:00 2001 From: Leo Vu <18277920+vunguyenhung@users.noreply.github.com> Date: Fri, 24 May 2024 12:12:23 +0700 Subject: [PATCH 7/8] Update components/hippo_video/actions/send-personalization-request/send-personalization-request.mjs --- .../send-personalization-request.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/hippo_video/actions/send-personalization-request/send-personalization-request.mjs b/components/hippo_video/actions/send-personalization-request/send-personalization-request.mjs index 4688fbd3a352b..df77cb9ca37e2 100644 --- a/components/hippo_video/actions/send-personalization-request/send-personalization-request.mjs +++ b/components/hippo_video/actions/send-personalization-request/send-personalization-request.mjs @@ -21,7 +21,7 @@ export default { file: { type: "string", label: "File", - description: "csv, xls, and xlsx file saved to the `/tmp` directory (e.g. `/tmp/example.jpg`). [See the documentation](https://pipedream.com/docs/workflows/steps/code/nodejs/working-with-files/#the-tmp-directory) which has data for video personalization.", + description: "csv, xls, and xlsx file saved to the [`/tmp` directory](https://pipedream.com/docs/workflows/steps/code/nodejs/working-with-files/#the-tmp-directory). To get file schema, [see documentation](https://help.hippovideo.io/support/solutions/articles/19000099793-bulk-video-personalization-and-tracking-api)", }, }, async run({ $ }) { From 69d10f72987cb385df66235215e9dc223b78d63e Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Fri, 24 May 2024 18:57:59 -0300 Subject: [PATCH 8/8] some adjusts --- .../send-personalization-request.mjs | 2 +- .../hippo_video/actions/upload-video/upload-video.mjs | 2 +- components/hippo_video/hippo_video.app.mjs | 7 +++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/components/hippo_video/actions/send-personalization-request/send-personalization-request.mjs b/components/hippo_video/actions/send-personalization-request/send-personalization-request.mjs index df77cb9ca37e2..9dfe67b622905 100644 --- a/components/hippo_video/actions/send-personalization-request/send-personalization-request.mjs +++ b/components/hippo_video/actions/send-personalization-request/send-personalization-request.mjs @@ -38,7 +38,7 @@ export default { maxBodyLength: Infinity, }); - if (!response.code != 200) throw new ConfigurationError(response.message || response.type); + if (response.code != 200) throw new ConfigurationError(response.message || response.type); $.export("$summary", `Successfully sent personalization request for video Id: ${this.videoId}`); return response; diff --git a/components/hippo_video/actions/upload-video/upload-video.mjs b/components/hippo_video/actions/upload-video/upload-video.mjs index ffec45f81a555..847aa58e9994e 100644 --- a/components/hippo_video/actions/upload-video/upload-video.mjs +++ b/components/hippo_video/actions/upload-video/upload-video.mjs @@ -29,7 +29,7 @@ export default { }, }); - if (!response.status) throw new ConfigurationError(response.message); + if (response.code != "200") throw new ConfigurationError(response.message); $.export("$summary", `Video uploaded successfully with Id: ${response.video_id}`); return response; diff --git a/components/hippo_video/hippo_video.app.mjs b/components/hippo_video/hippo_video.app.mjs index e9e57da9a43a8..e6e6ff9448dc9 100644 --- a/components/hippo_video/hippo_video.app.mjs +++ b/components/hippo_video/hippo_video.app.mjs @@ -85,6 +85,13 @@ export default { ...opts, }); }, + personalizeVideo(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/api/v1/me/video/bulk_personalize", + ...opts, + }); + }, uploadVideo(opts = {}) { return this._makeRequest({ method: "POST",