@voxygen/voxygen-tts 1.3.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.
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@voxygen/voxygen-tts",
3
+ "version": "1.3.0",
4
+ "description": "",
5
+ "keywords": [
6
+ "tts"
7
+ ],
8
+ "homepage": "https://github.com/katell-qmnr-voxygen/voxygen-js-client-test#readme",
9
+ "bugs": {
10
+ "url": "https://github.com/katell-qmnr-voxygen/voxygen-js-client-test/issues"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/katell-qmnr-voxygen/voxygen-js-client-test.git"
15
+ },
16
+ "license": "ISC",
17
+ "author": "",
18
+ "type": "commonjs",
19
+ "main": "voxygen-tts/client.mjs",
20
+ "scripts": {
21
+ "test": "echo \"Error: no test specified\" && exit 1"
22
+ },
23
+ "publishConfig": {
24
+ "access": "restricted"
25
+ }
26
+ }
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+
3
+ export default class SyntHTTPClient {
4
+ static MimeType = Object.freeze({
5
+ PLAIN_TEXT: Symbol("PLAIN_TEXT"),
6
+ AUDIO: Symbol("AUDIO"),
7
+ JSON: Symbol("JSON"),
8
+ URL_ENCODED: Symbol("URL_ENCODED"),
9
+ MULTIPART: Symbol("MULTIPART")
10
+ })
11
+ #token
12
+ #url
13
+ #max_retries
14
+ #retry_after
15
+ #body_type
16
+ #accept_type
17
+ constructor(token, url = 'https://api.voxygen.fr/tts') {
18
+ if (typeof token !== 'string' || token === '')
19
+ throw new Error("token must be provided");
20
+ this.#token = token;
21
+ this.#url = new URL(url);
22
+ // default retry policy : 6 times with a 10 second wait
23
+ this.setRetryPolicy(6, 10 /* seconds */);
24
+ // default request content type : JSON
25
+ this.setRequestContentType(SyntHTTPClient.MimeType.JSON);
26
+ // default accept content type : AUDIO
27
+ this.setAcceptContentType(SyntHTTPClient.MimeType.AUDIO);
28
+ }
29
+
30
+ setRetryPolicy(max_retries, retry_after) {
31
+ this.#max_retries = max_retries;
32
+ this.#retry_after = retry_after;
33
+ }
34
+
35
+ setRequestContentType(mime_type) {
36
+ this.#body_type = mime_type;
37
+ }
38
+
39
+ setAcceptContentType(mime_type) {
40
+ this.#accept_type = mime_type;
41
+ }
42
+
43
+ async buildRequest(anObject) {
44
+ let request = {};
45
+ // initialize query from host url
46
+ this.#url.searchParams.forEach((value, key) => request[key] = value);
47
+ this.#url.search = ""; // empty URL search, since it's now in the request body
48
+ // add arguments to request query (argument values take priority over existing parameters)
49
+ for (const [key, value] of Object.entries(anObject)) {
50
+ request[key] = value;
51
+ }
52
+ // NOTE: rfc2046 section-4.1.1 "MUST always represent a line break as a CRLF sequence"
53
+ for (const [key, value] of Object.entries(request)) {
54
+ if (typeof value === 'string')
55
+ request[key] = value.replace(/(\r?\n)/g, '\r\n');
56
+ }
57
+ return request;
58
+ }
59
+
60
+ #sleep = (sec) => new Promise((resolve) => setTimeout(() => resolve(), sec * 1000))
61
+
62
+ fetch(aRequest, options = {}) {
63
+ options.method = "POST";
64
+ if (!options.headers)
65
+ options.headers = new Headers();
66
+ options.headers.append("User-Agent", "Voxygen-Client/1.3 (javascript)");
67
+ let aBody;
68
+ switch (this.#body_type) {
69
+ case SyntHTTPClient.MimeType.JSON:
70
+ aBody = JSON.stringify(aRequest);
71
+ // NOTE: rfc8259 section-8.1 "JSON text exchanged between systems that are not part of a closed ecosystem MUST be encoded using UTF-8"
72
+ options.headers.append("Content-Type", "application/json");
73
+ break;
74
+ case SyntHTTPClient.MimeType.MULTIPART:
75
+ aBody = new FormData(); // fetch() will produce multipart/form-data Content-Type from a FormData body
76
+ for (const [key, value] of Object.entries(aRequest)) {
77
+ aBody.append(key, value);
78
+ }
79
+ break;
80
+ case SyntHTTPClient.MimeType.URL_ENCODED:
81
+ aBody = new URLSearchParams(); // fetch() will produce application/x-www-form-urlencoded Content-Type from a URLSearchParams body
82
+ for (const [key, value] of Object.entries(aRequest)) {
83
+ aBody.append(key, value);
84
+ }
85
+ break;
86
+ default:
87
+ throw new Error(`unsupported body type`);
88
+ break;
89
+ }
90
+ switch (this.#accept_type) {
91
+ case SyntHTTPClient.MimeType.AUDIO:
92
+ options.headers.append("Accept", "audio/*; q=1.0, application/octet-stream; q=0.8, */*; q=0.1");
93
+ break;
94
+ case SyntHTTPClient.MimeType.JSON:
95
+ options.headers.append("Accept", "application/json, */*; q=0.1");
96
+ break;
97
+ default:
98
+ throw new Error(`unsupported accept content type`);
99
+ break;
100
+ }
101
+ options.headers.append("Authorization", `Bearer ${this.#token}`);
102
+ options.body = aBody;
103
+ return new Promise((resolve, reject) => {
104
+ const wrapper = (n) => {
105
+ fetch(this.#url.toString(), options)
106
+ .then((response) => {
107
+ if (response.status == 503 /* Service Unavailable */ && n) {
108
+ this.#sleep(this.#retry_after).then(() => wrapper(--n));
109
+ } else {
110
+ resolve(response);
111
+ }
112
+ })
113
+ .catch(async (err) => reject(err))
114
+ };
115
+ wrapper(this.#max_retries);
116
+ });
117
+ }
118
+
119
+ getContentType(aResponse) {
120
+ const contentType = aResponse.headers.get("Content-Type");
121
+ if (contentType != null) {
122
+ if (contentType.startsWith("audio/") || contentType === 'application/octet-stream') {
123
+ return SyntHTTPClient.MimeType.AUDIO;
124
+ } else if (contentType === 'application/json') {
125
+ return SyntHTTPClient.MimeType.JSON;
126
+ }
127
+ }
128
+ return SyntHTTPClient.MimeType.PLAIN_TEXT;
129
+ }
130
+
131
+ }