@pipedream/sonix 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,37 @@
1
+ import { LANGUAGE_OPTIONS } from "../../common/constants.mjs";
2
+ import sonix from "../../sonix.app.mjs";
3
+
4
+ export default {
5
+ key: "sonix-create-new-translation",
6
+ name: "Create New Translation",
7
+ description: "Creates a new translation for a selected media file. [See the documentation](https://sonix.ai/docs/api#create_translation)",
8
+ version: "0.0.1",
9
+ type: "action",
10
+ props: {
11
+ sonix,
12
+ mediaId: {
13
+ propDefinition: [
14
+ sonix,
15
+ "mediaId",
16
+ ],
17
+ },
18
+ language: {
19
+ type: "string",
20
+ label: "Language",
21
+ description: "Language code for the translation",
22
+ options: LANGUAGE_OPTIONS,
23
+ },
24
+ },
25
+ async run({ $ }) {
26
+ const response = await this.sonix.createTranslation({
27
+ $,
28
+ mediaId: this.mediaId,
29
+ data: {
30
+ language: this.language,
31
+ },
32
+ });
33
+
34
+ $.export("$summary", `Successfully created translation for media ID: ${this.mediaId}`);
35
+ return response;
36
+ },
37
+ };
@@ -0,0 +1,23 @@
1
+ import sonix from "../../sonix.app.mjs";
2
+
3
+ export default {
4
+ key: "sonix-get-text-transcript",
5
+ name: "Get Text Transcript",
6
+ description: "Gets the text transcript of a selected media file. [See the documentation](https://sonix.ai/docs/api#get_transcript)",
7
+ version: "0.0.1",
8
+ type: "action",
9
+ props: {
10
+ sonix,
11
+ mediaId: {
12
+ propDefinition: [
13
+ sonix,
14
+ "mediaId",
15
+ ],
16
+ },
17
+ },
18
+ async run({ $ }) {
19
+ const response = await this.sonix.getTextTranscript(this.mediaId);
20
+ $.export("$summary", `Successfully fetched transcript for media ID: ${this.mediaId}`);
21
+ return response;
22
+ },
23
+ };
@@ -0,0 +1,100 @@
1
+ import { ConfigurationError } from "@pipedream/platform";
2
+ import FormData from "form-data";
3
+ import fs from "fs";
4
+ import { LANGUAGE_OPTIONS } from "../../common/constants.mjs";
5
+ import { checkTmp } from "../../common/utils.mjs";
6
+ import sonix from "../../sonix.app.mjs";
7
+
8
+ export default {
9
+ key: "sonix-upload-media",
10
+ name: "Upload Media",
11
+ description: "Submits new media for processing. [See the documentation](https://sonix.ai/docs/api#new_media)",
12
+ version: "0.0.1",
13
+ type: "action",
14
+ props: {
15
+ sonix,
16
+ file: {
17
+ type: "string",
18
+ label: "File",
19
+ description: "Path of the audio or video file in /tmp folder. The limit is 100MB using this parameter. For larger files, use **File URL**. `NOTE: Only one of **File** or **File URL** is required.` To upload a file to /tmp folder, please follow the [doc here](https://pipedream.com/docs/code/nodejs/working-with-files/#writing-a-file-to-tmp)",
20
+ optional: true,
21
+ },
22
+ fileUrl: {
23
+ type: "string",
24
+ label: "File URL",
25
+ description: "URL pointing to the audio/video file. `NOTE: Only one of **File** or **File URL** is required.`",
26
+ optional: true,
27
+ },
28
+ language: {
29
+ type: "string",
30
+ label: "Language",
31
+ description: "Language code for the transcription.",
32
+ options: LANGUAGE_OPTIONS,
33
+ },
34
+ folderId: {
35
+ propDefinition: [
36
+ sonix,
37
+ "folderId",
38
+ ],
39
+ optional: true,
40
+ },
41
+ name: {
42
+ type: "string",
43
+ label: "Name",
44
+ description: "Name of the file in Sonix. If no name is provided, we will default to the filename.",
45
+ optional: true,
46
+ },
47
+ transcriptText: {
48
+ type: "boolean",
49
+ label: "Transcript Text",
50
+ description: "Existing transcript - if present, will align the transcript rather than transcribing.",
51
+ optional: true,
52
+ },
53
+ keywords: {
54
+ type: "string[]",
55
+ label: "Keywords",
56
+ description: "Comma separated list of words or phrases to use as hints to the transcription engine. If this is provided, then the account level keywords will not be used.",
57
+ optional: true,
58
+ },
59
+ customData: {
60
+ type: "object",
61
+ label: "Custom Data",
62
+ description: "Set of key-value pairs that you can attach to the media. This can be useful for storing additional information about file.",
63
+ optional: true,
64
+ },
65
+ callbackUrl: {
66
+ type: "string",
67
+ label: "Callback URL",
68
+ description: "URL for Sonix to make a POST request notifying of a change in transcript status (either failed or completed). The POST will include the media status JSON.",
69
+ optional: true,
70
+ },
71
+ },
72
+ async run({ $ }) {
73
+ if (!this.file && !this.fileUrl) {
74
+ throw new ConfigurationError("You must provite whether **File** or **File URL**.");
75
+ }
76
+
77
+ const formData = new FormData();
78
+
79
+ if (this.file) {
80
+ const filePath = checkTmp(this.file);
81
+ formData.append("file", fs.createReadStream(filePath));
82
+ }
83
+
84
+ this.fileUrl && formData.append("file_url", this.fileUrl);
85
+ this.language && formData.append("language", this.language);
86
+ this.name && formData.append("name", this.name);
87
+ this.transcriptText && formData.append("transcript_text", `${this.transcriptText}`);
88
+ this.folderId && formData.append("folder_id", this.folderId);
89
+ this.keywords && formData.append("keywords", this.keywords.toString());
90
+ this.customData && formData.append("custom_data", JSON.stringify(this.customData));
91
+ this.callbackUrl && formData.append("callback_url", this.callbackUrl);
92
+
93
+ const response = await this.sonix.submitNewMedia({
94
+ data: formData,
95
+ headers: formData.getHeaders(),
96
+ });
97
+ $.export("$summary", `Successfully uploaded media with ID: ${response.id}`);
98
+ return response;
99
+ },
100
+ };
@@ -0,0 +1,154 @@
1
+ export const LANGUAGE_OPTIONS = [
2
+ {
3
+ label: "English",
4
+ value: "en",
5
+ },
6
+ {
7
+ label: "French",
8
+ value: "fr",
9
+ },
10
+ {
11
+ label: "German",
12
+ value: "de",
13
+ },
14
+ {
15
+ label: "Spanish",
16
+ value: "es",
17
+ },
18
+ {
19
+ label: "Arabic",
20
+ value: "ar",
21
+ },
22
+ {
23
+ label: "Armenian",
24
+ value: "hy-AM",
25
+ },
26
+ {
27
+ label: "Bulgarian",
28
+ value: "bg",
29
+ },
30
+ {
31
+ label: "Catalan",
32
+ value: "ca",
33
+ },
34
+ {
35
+ label: "Croatian",
36
+ value: "hr",
37
+ },
38
+ {
39
+ label: "Chinese (Cantonese)",
40
+ value: "yue-Hant-HK",
41
+ },
42
+ {
43
+ label: "Chinese (Mandarin)",
44
+ value: "cmn-Hans-CN",
45
+ },
46
+ {
47
+ label: "Czech",
48
+ value: "cs",
49
+ },
50
+ {
51
+ label: "Danish",
52
+ value: "da",
53
+ },
54
+ {
55
+ label: "Dutch",
56
+ value: "nl",
57
+ },
58
+ {
59
+ label: "Finnish",
60
+ value: "fi",
61
+ },
62
+ {
63
+ label: "Greek",
64
+ value: "el",
65
+ },
66
+ {
67
+ label: "Hebrew",
68
+ value: "he-IL",
69
+ },
70
+ {
71
+ label: "Hindi",
72
+ value: "hi",
73
+ },
74
+ {
75
+ label: "Hungarian",
76
+ value: "hu",
77
+ },
78
+ {
79
+ label: "Indonesian",
80
+ value: "id-ID",
81
+ },
82
+ {
83
+ label: "Italian",
84
+ value: "it",
85
+ },
86
+ {
87
+ label: "Japanese",
88
+ value: "ja",
89
+ },
90
+ {
91
+ label: "Korean",
92
+ value: "ko",
93
+ },
94
+ {
95
+ label: "Latvian",
96
+ value: "lv",
97
+ },
98
+ {
99
+ label: "Lithuanian",
100
+ value: "lt",
101
+ },
102
+ {
103
+ label: "Malay",
104
+ value: "ms-MY",
105
+ },
106
+ {
107
+ label: "Norwegian",
108
+ value: "nb-NO",
109
+ },
110
+ {
111
+ label: "Polish",
112
+ value: "pl",
113
+ },
114
+ {
115
+ label: "Portuguese",
116
+ value: "pt",
117
+ },
118
+ {
119
+ label: "Romanian",
120
+ value: "ro",
121
+ },
122
+ {
123
+ label: "Russian",
124
+ value: "ru",
125
+ },
126
+ {
127
+ label: "Slovak",
128
+ value: "sk",
129
+ },
130
+ {
131
+ label: "Slovenian",
132
+ value: "sl",
133
+ },
134
+ {
135
+ label: "Swedish",
136
+ value: "sv",
137
+ },
138
+ {
139
+ label: "Thai",
140
+ value: "th-TH",
141
+ },
142
+ {
143
+ label: "Turkish",
144
+ value: "tr-TR",
145
+ },
146
+ {
147
+ label: "Ukrainian",
148
+ value: "uk",
149
+ },
150
+ {
151
+ label: "Vietnamese",
152
+ value: "vi-VN",
153
+ },
154
+ ];
@@ -0,0 +1,6 @@
1
+ export const checkTmp = (filename) => {
2
+ if (filename.indexOf("/tmp") === -1) {
3
+ return `/tmp/${filename}`;
4
+ }
5
+ return filename;
6
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pipedream/sonix",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "description": "Pipedream Sonix Components",
5
5
  "main": "sonix.app.mjs",
6
6
  "keywords": [
@@ -11,5 +11,10 @@
11
11
  "author": "Pipedream <support@pipedream.com> (https://pipedream.com/)",
12
12
  "publishConfig": {
13
13
  "access": "public"
14
+ },
15
+ "dependencies": {
16
+ "@pipedream/platform": "^1.5.1",
17
+ "form-data": "^4.0.0",
18
+ "fs": "^0.0.1-security"
14
19
  }
15
20
  }
package/sonix.app.mjs CHANGED
@@ -1,11 +1,129 @@
1
+ import { axios } from "@pipedream/platform";
2
+
1
3
  export default {
2
4
  type: "app",
3
5
  app: "sonix",
4
- propDefinitions: {},
6
+ propDefinitions: {
7
+ folderId: {
8
+ type: "string",
9
+ label: "Folder ID",
10
+ description: "ID of folder to place the media in",
11
+ async options() {
12
+ const { folders } = await this.listFolders();
13
+
14
+ return folders.map(({
15
+ id: value, name, email,
16
+ }) => ({
17
+ label: name || email,
18
+ value,
19
+ }));
20
+ },
21
+ },
22
+ mediaId: {
23
+ type: "string",
24
+ label: "Media ID",
25
+ description: "ID of the media file",
26
+ async options({ page }) {
27
+ const { media } = await this.listMedia({
28
+ params: {
29
+ page: page + 1,
30
+ status: "completed",
31
+ },
32
+ });
33
+
34
+ return media.map(({
35
+ name: label, id: value,
36
+ }) => ({
37
+ label,
38
+ value,
39
+ }));
40
+ },
41
+ },
42
+ },
5
43
  methods: {
6
- // this.$auth contains connected account data
7
- authKeys() {
8
- console.log(Object.keys(this.$auth));
44
+ _getApiKey() {
45
+ return this.$auth.api_key;
46
+ },
47
+ _baseUrl() {
48
+ return "https://api.sonix.ai/v1";
49
+ },
50
+ _getHeaders(headers) {
51
+ return {
52
+ ...headers,
53
+ Authorization: `Bearer ${this._getApiKey()}`,
54
+ };
55
+ },
56
+ _makeRequest({
57
+ $ = this, path, headers, ...otherOpts
58
+ }) {
59
+ const config = {
60
+ ...otherOpts,
61
+ url: this._baseUrl() + path,
62
+ headers: this._getHeaders(headers),
63
+ };
64
+
65
+ return axios($, config);
66
+ },
67
+ listFolders() {
68
+ return this._makeRequest({
69
+ path: "/folders",
70
+ });
71
+ },
72
+ listMedia(opts = {}) {
73
+ return this._makeRequest({
74
+ ...opts,
75
+ path: "/media",
76
+ });
77
+ },
78
+ submitNewMedia(opts = {}) {
79
+ return this._makeRequest({
80
+ ...opts,
81
+ method: "POST",
82
+ path: "/media",
83
+ });
84
+ },
85
+ getTextTranscript(mediaId) {
86
+ return this._makeRequest({
87
+ path: `/media/${mediaId}/transcript`,
88
+ });
89
+ },
90
+ createTranslation({
91
+ mediaId, ...opts
92
+ }) {
93
+ return this._makeRequest({
94
+ ...opts,
95
+ method: "POST",
96
+ path: `/media/${mediaId}/translations`,
97
+ });
98
+ },
99
+ async *paginate({
100
+ fn, params = {}, maxResults = null,
101
+ }) {
102
+ let hasMore = false;
103
+ let count = 0;
104
+ let page = 0;
105
+
106
+ do {
107
+ params.page = ++page;
108
+ const {
109
+ media,
110
+ page: currentPage,
111
+ total_pages: lastPage,
112
+ } = await fn({
113
+ params,
114
+ });
115
+
116
+ for (const d of media) {
117
+ yield d;
118
+
119
+ if (maxResults && ++count === maxResults) {
120
+ return count;
121
+ }
122
+ }
123
+
124
+ hasMore = currentPage != lastPage;
125
+
126
+ } while (hasMore);
9
127
  },
10
128
  },
11
- };
129
+ };
@@ -0,0 +1,66 @@
1
+ import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform";
2
+ import sonix from "../../sonix.app.mjs";
3
+
4
+ export default {
5
+ key: "sonix-media-upload-complete",
6
+ name: "New Media Upload Complete",
7
+ description: "Emit new event any time the media status of an item changes to completed.",
8
+ version: "0.0.1",
9
+ type: "source",
10
+ dedupe: "unique",
11
+ props: {
12
+ sonix,
13
+ db: "$.service.db",
14
+ timer: {
15
+ type: "$.interface.timer",
16
+ default: {
17
+ intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL,
18
+ },
19
+ },
20
+ },
21
+ methods: {
22
+ _getLastDate() {
23
+ return this.db.get("lastDate") ?? 0;
24
+ },
25
+ _setLastDate(status) {
26
+ this.db.set("lastDate", status);
27
+ },
28
+ async startEvent(maxResults = 0) {
29
+ const lastDate = this._getLastDate();
30
+ const items = this.sonix.paginate({
31
+ fn: this.sonix.listMedia,
32
+ maxResults,
33
+ params: {
34
+ status: "completed",
35
+ },
36
+ });
37
+
38
+ let responseArray = [];
39
+
40
+ for await (const item of items) {
41
+ if (new Date(item.created_at) <= new Date(lastDate)) break;
42
+ responseArray.push(item);
43
+ }
44
+ if (responseArray.length) this._setLastDate(responseArray[0].created_at);
45
+
46
+ for (const item of responseArray.reverse()) {
47
+ this.$emit(
48
+ item,
49
+ {
50
+ id: item.id,
51
+ summary: `Media upload completed: ${item.name}`,
52
+ ts: Date.parse(item.created_at),
53
+ },
54
+ );
55
+ }
56
+ },
57
+ },
58
+ hooks: {
59
+ async deploy() {
60
+ await this.startEvent(25);
61
+ },
62
+ },
63
+ async run() {
64
+ await this.startEvent();
65
+ },
66
+ };