@pie-players/pie-tool-protractor 0.3.43 → 0.3.45
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/README.md +1 -1
- package/dist/{dist-DIBQdekL.js → dist-DfQrc9XY.js} +198 -173
- package/dist/{dist-rF11IR1B.js → dist-Dns3leWh.js} +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/sre-Cy20lES5.js +17534 -0
- package/dist/tool-protractor.js +8277 -5060
- package/package.json +5 -8
- package/dist/index.d.ts.map +0 -1
- package/tool-protractor.svelte +0 -358
- /package/dist/{defineProperty-CyepwRr5.js → defineProperty-Dt7ri80W.js} +0 -0
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ A draggable and rotatable protractor overlay tool for geometry and measurement q
|
|
|
18
18
|
|
|
19
19
|
```svelte
|
|
20
20
|
<script>
|
|
21
|
-
import '@pie-players/pie-tool-protractor
|
|
21
|
+
import '@pie-players/pie-tool-protractor';
|
|
22
22
|
import { toolCoordinator } from '@pie-players/pie-assessment-toolkit';
|
|
23
23
|
|
|
24
24
|
let showProtractor = $derived(
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
function
|
|
3
|
-
if (typeof
|
|
1
|
+
import { t as d } from "./defineProperty-Dt7ri80W.js";
|
|
2
|
+
function T(t, e) {
|
|
3
|
+
if (typeof t != "string" || t.length === 0) return null;
|
|
4
4
|
try {
|
|
5
|
-
const r = typeof
|
|
5
|
+
const r = typeof e.apiEndpoint == "string" && /^https?:\/\//i.test(e.apiEndpoint) ? e.apiEndpoint : void 0, i = r ? new URL(t, r) : new URL(t);
|
|
6
6
|
return i.protocol !== "http:" && i.protocol !== "https:" ? null : i;
|
|
7
7
|
} catch {
|
|
8
8
|
return null;
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
|
-
function
|
|
12
|
-
const r = Array.isArray(
|
|
11
|
+
function A(t, e) {
|
|
12
|
+
const r = Array.isArray(e.assetOrigins) ? e.assetOrigins.filter((i) => typeof i == "string" && i.length > 0).map((i) => {
|
|
13
13
|
try {
|
|
14
14
|
return new URL(i).origin;
|
|
15
15
|
} catch {
|
|
@@ -17,85 +17,89 @@ function k(e, t) {
|
|
|
17
17
|
}
|
|
18
18
|
}) : [];
|
|
19
19
|
if (r.length === 0) {
|
|
20
|
-
if (typeof
|
|
21
|
-
return new URL(
|
|
20
|
+
if (typeof e.apiEndpoint == "string" && /^https?:\/\//i.test(e.apiEndpoint)) try {
|
|
21
|
+
return new URL(e.apiEndpoint).origin === t.origin;
|
|
22
22
|
} catch {
|
|
23
23
|
return !1;
|
|
24
24
|
}
|
|
25
|
-
return typeof window < "u" && typeof window.location?.origin == "string" && window.location.origin ===
|
|
25
|
+
return typeof window < "u" && typeof window.location?.origin == "string" && window.location.origin === t.origin;
|
|
26
26
|
}
|
|
27
|
-
return r.includes(
|
|
27
|
+
return r.includes(t.origin);
|
|
28
28
|
}
|
|
29
|
-
function
|
|
30
|
-
const
|
|
31
|
-
for (const [r, i] of Object.entries(
|
|
29
|
+
function R(t) {
|
|
30
|
+
const e = {};
|
|
31
|
+
for (const [r, i] of Object.entries(t)) {
|
|
32
32
|
const o = r.toLowerCase();
|
|
33
|
-
o === "authorization" || o === "proxy-authorization" || o === "cookie" || o.startsWith("x-auth-") || (
|
|
33
|
+
o === "authorization" || o === "proxy-authorization" || o === "cookie" || o.startsWith("x-auth-") || (e[r] = i);
|
|
34
34
|
}
|
|
35
|
-
return
|
|
35
|
+
return e;
|
|
36
36
|
}
|
|
37
|
-
var
|
|
38
|
-
const
|
|
39
|
-
return typeof
|
|
40
|
-
},
|
|
37
|
+
var I = (t) => {
|
|
38
|
+
const e = (t.providerOptions && typeof t.providerOptions == "object" ? t.providerOptions : {}).__pieTelemetry;
|
|
39
|
+
return typeof e == "function" ? e : void 0;
|
|
40
|
+
}, E = {
|
|
41
41
|
pie: 3e3,
|
|
42
42
|
custom: 3e3
|
|
43
|
-
},
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
43
|
+
}, M = /* @__PURE__ */ new Set(["polly", "google"]), P = (t) => {
|
|
44
|
+
if (!t || b(t) !== "pie") return !1;
|
|
45
|
+
const e = (t.provider || "polly").toLowerCase();
|
|
46
|
+
return M.has(e);
|
|
47
|
+
}, S = (t) => t.replace(/\/+$/, ""), O = (t) => {
|
|
48
|
+
const e = S(t.apiEndpoint), r = (t.provider || "").toLowerCase();
|
|
49
|
+
return r === "polly" || r === "google" ? `${e}/${r}/voices` : `${e}/voices`;
|
|
50
|
+
}, b = (t) => t.transportMode === "custom" ? "custom" : t.transportMode === "pie" ? "pie" : t.provider === "custom" ? "custom" : "pie", k = (t, e) => t.endpointMode ? t.endpointMode : e === "custom" ? "rootPost" : "synthesizePath", U = (t, e) => t.endpointValidationMode ? t.endpointValidationMode : e === "custom" ? "none" : "voices", L = (t) => {
|
|
51
|
+
const e = t.providerOptions || {};
|
|
52
|
+
if (typeof e.speedRate == "string") return e.speedRate;
|
|
53
|
+
const r = Number(t.rate ?? 1);
|
|
50
54
|
return !Number.isFinite(r) || r <= 0.95 ? "slow" : r >= 1.5 ? "fast" : "medium";
|
|
51
|
-
},
|
|
52
|
-
const
|
|
55
|
+
}, B = (t) => {
|
|
56
|
+
const e = [];
|
|
53
57
|
let r = 0;
|
|
54
|
-
const i =
|
|
58
|
+
const i = t.split(`
|
|
55
59
|
`).map((o) => o.trim()).filter(Boolean);
|
|
56
60
|
for (const o of i) try {
|
|
57
|
-
const s = JSON.parse(o), a = typeof s.type == "string" ? s.type : "word",
|
|
58
|
-
r = Math.max(c + 1, r),
|
|
59
|
-
time:
|
|
61
|
+
const s = JSON.parse(o), a = typeof s.type == "string" ? s.type : "word", n = typeof s.time == "number" && Number.isFinite(s.time) ? s.time : 0, h = typeof s.value == "string" ? s.value : "", l = typeof s.start == "number" && Number.isFinite(s.start) ? s.start : null, u = typeof s.end == "number" && Number.isFinite(s.end) ? s.end : null, y = l ?? r, c = u ?? y + Math.max(1, h.length || String(s.value || "").length);
|
|
62
|
+
r = Math.max(c + 1, r), e.push({
|
|
63
|
+
time: n,
|
|
60
64
|
type: a,
|
|
61
|
-
start:
|
|
65
|
+
start: y,
|
|
62
66
|
end: c,
|
|
63
|
-
value:
|
|
67
|
+
value: h
|
|
64
68
|
});
|
|
65
69
|
} catch {
|
|
66
70
|
}
|
|
67
|
-
return
|
|
68
|
-
}, C = (
|
|
69
|
-
if (!Array.isArray(
|
|
70
|
-
const
|
|
71
|
+
return e;
|
|
72
|
+
}, C = (t) => {
|
|
73
|
+
if (!Array.isArray(t)) return [];
|
|
74
|
+
const e = [];
|
|
71
75
|
let r = 0;
|
|
72
|
-
for (const i of
|
|
76
|
+
for (const i of t) {
|
|
73
77
|
if (!i || typeof i != "object") continue;
|
|
74
|
-
const o = typeof i.type == "string" ? i.type : "word", s = typeof i.time == "number" && Number.isFinite(i.time) ? i.time : 0, a = typeof i.value == "string" ? i.value : "",
|
|
75
|
-
r = Math.max(r, u + 1),
|
|
78
|
+
const o = typeof i.type == "string" ? i.type : "word", s = typeof i.time == "number" && Number.isFinite(i.time) ? i.time : 0, a = typeof i.value == "string" ? i.value : "", n = typeof i.start == "number" && Number.isFinite(i.start) ? i.start : null, h = typeof i.end == "number" && Number.isFinite(i.end) ? i.end : null, l = n ?? r, u = h ?? l + Math.max(1, a.length || 1);
|
|
79
|
+
r = Math.max(r, u + 1), e.push({
|
|
76
80
|
time: s,
|
|
77
81
|
type: o,
|
|
78
|
-
start:
|
|
82
|
+
start: l,
|
|
79
83
|
end: u,
|
|
80
84
|
value: a
|
|
81
85
|
});
|
|
82
86
|
}
|
|
83
|
-
return
|
|
84
|
-
},
|
|
87
|
+
return e.sort((i, o) => i.time !== o.time ? i.time - o.time : i.start !== o.start ? i.start - o.start : i.end - o.end);
|
|
88
|
+
}, z = {
|
|
85
89
|
pie: {
|
|
86
90
|
id: "pie",
|
|
87
|
-
resolveSynthesisUrl: (
|
|
88
|
-
const
|
|
89
|
-
return
|
|
91
|
+
resolveSynthesisUrl: (t) => {
|
|
92
|
+
const e = k(t, "pie"), r = S(t.apiEndpoint);
|
|
93
|
+
return e === "rootPost" ? r : `${r}/synthesize`;
|
|
90
94
|
},
|
|
91
|
-
buildRequestBody: (
|
|
92
|
-
const r =
|
|
95
|
+
buildRequestBody: (t, e) => {
|
|
96
|
+
const r = e.providerOptions || {}, i = typeof e.engine == "string" ? e.engine : typeof r.engine == "string" ? r.engine : void 0, o = typeof r.sampleRate == "number" && Number.isFinite(r.sampleRate) ? r.sampleRate : void 0, s = r.format === "mp3" || r.format === "ogg" || r.format === "pcm" ? r.format : void 0, a = Array.isArray(r.speechMarkTypes) ? r.speechMarkTypes.filter((n) => n === "word" || n === "sentence" || n === "ssml") : void 0;
|
|
93
97
|
return {
|
|
94
|
-
text:
|
|
95
|
-
provider:
|
|
96
|
-
voice:
|
|
97
|
-
language:
|
|
98
|
-
rate:
|
|
98
|
+
text: t,
|
|
99
|
+
provider: e.provider || "polly",
|
|
100
|
+
voice: e.voice,
|
|
101
|
+
language: e.language,
|
|
102
|
+
rate: e.rate,
|
|
99
103
|
engine: i,
|
|
100
104
|
sampleRate: o,
|
|
101
105
|
format: s,
|
|
@@ -103,100 +107,96 @@ var R = (e) => {
|
|
|
103
107
|
includeSpeechMarks: !0
|
|
104
108
|
};
|
|
105
109
|
},
|
|
106
|
-
parseResponse: async (
|
|
107
|
-
const
|
|
110
|
+
parseResponse: async (t) => {
|
|
111
|
+
const e = await t.json();
|
|
108
112
|
return {
|
|
109
113
|
audio: {
|
|
110
114
|
kind: "base64",
|
|
111
|
-
data:
|
|
112
|
-
contentType:
|
|
115
|
+
data: e.audio,
|
|
116
|
+
contentType: e.contentType
|
|
113
117
|
},
|
|
114
|
-
speechMarks: Array.isArray(
|
|
118
|
+
speechMarks: Array.isArray(e.speechMarks) ? e.speechMarks : []
|
|
115
119
|
};
|
|
116
120
|
}
|
|
117
121
|
},
|
|
118
122
|
custom: {
|
|
119
123
|
id: "custom",
|
|
120
|
-
resolveSynthesisUrl: (
|
|
121
|
-
const
|
|
122
|
-
return
|
|
124
|
+
resolveSynthesisUrl: (t) => {
|
|
125
|
+
const e = k(t, "custom"), r = S(t.apiEndpoint);
|
|
126
|
+
return e === "synthesizePath" ? `${r}/synthesize` : r;
|
|
123
127
|
},
|
|
124
|
-
buildRequestBody: (
|
|
125
|
-
const r =
|
|
128
|
+
buildRequestBody: (t, e) => {
|
|
129
|
+
const r = e.providerOptions || {}, i = typeof r.lang_id == "string" ? r.lang_id : e.language || "en-US", o = typeof r.cache == "boolean" ? r.cache : !0;
|
|
126
130
|
return {
|
|
127
|
-
text:
|
|
128
|
-
speedRate:
|
|
131
|
+
text: t,
|
|
132
|
+
speedRate: L(e),
|
|
129
133
|
lang_id: i,
|
|
130
134
|
cache: o
|
|
131
135
|
};
|
|
132
136
|
},
|
|
133
|
-
parseResponse: async (
|
|
134
|
-
const o = await
|
|
135
|
-
if (
|
|
136
|
-
for (const [
|
|
137
|
+
parseResponse: async (t, e, r, i) => {
|
|
138
|
+
const o = await t.json(), s = {};
|
|
139
|
+
if (e.includeAuthOnAssetFetch)
|
|
140
|
+
for (const [l, u] of Object.entries(r)) l.toLowerCase() === "authorization" && (s[l] = u);
|
|
137
141
|
let a = [];
|
|
138
|
-
const
|
|
139
|
-
if (
|
|
142
|
+
const n = C(o.speechMarks);
|
|
143
|
+
if (n.length > 0) a = n;
|
|
140
144
|
else if (typeof o.word == "string" && o.word.length > 0) {
|
|
141
|
-
const
|
|
142
|
-
if (
|
|
143
|
-
const u =
|
|
145
|
+
const l = T(o.word, e);
|
|
146
|
+
if (l !== null) {
|
|
147
|
+
const u = A(l, e) ? s : R(s), y = await fetch(l.toString(), {
|
|
144
148
|
headers: u,
|
|
145
149
|
signal: i,
|
|
146
150
|
redirect: "error"
|
|
147
151
|
});
|
|
148
|
-
|
|
152
|
+
y.ok && (a = B(await y.text()));
|
|
149
153
|
}
|
|
150
154
|
}
|
|
151
|
-
const
|
|
152
|
-
if (
|
|
155
|
+
const h = T(o.audioContent, e);
|
|
156
|
+
if (h === null) throw new Error("TTS server returned an invalid audio URL");
|
|
153
157
|
return {
|
|
154
158
|
audio: {
|
|
155
159
|
kind: "url",
|
|
156
|
-
url:
|
|
160
|
+
url: h.toString()
|
|
157
161
|
},
|
|
158
162
|
speechMarks: a
|
|
159
163
|
};
|
|
160
164
|
}
|
|
161
165
|
}
|
|
162
|
-
},
|
|
163
|
-
constructor(
|
|
164
|
-
|
|
166
|
+
}, x = class {
|
|
167
|
+
constructor(t, e) {
|
|
168
|
+
d(this, "config", void 0), d(this, "adapter", void 0), d(this, "currentAudio", null), d(this, "pausedState", !1), d(this, "wordTimings", []), d(this, "highlightInterval", null), d(this, "intentionallyStopped", !1), d(this, "activeSynthesisController", null), d(this, "synthesisRunId", 0), d(this, "telemetryReporter", void 0), d(this, "onWordBoundary", void 0), this.config = t, this.adapter = e, this.telemetryReporter = I(t);
|
|
165
169
|
}
|
|
166
|
-
async emitTelemetry(
|
|
170
|
+
async emitTelemetry(t, e) {
|
|
167
171
|
try {
|
|
168
|
-
await this.telemetryReporter?.(
|
|
172
|
+
await this.telemetryReporter?.(t, e);
|
|
169
173
|
} catch (r) {
|
|
170
174
|
console.warn("[ServerTTSProvider] telemetry callback failed:", r);
|
|
171
175
|
}
|
|
172
176
|
}
|
|
173
|
-
async speak(
|
|
177
|
+
async speak(t) {
|
|
174
178
|
this.stop(), this.intentionallyStopped = !1;
|
|
175
|
-
const
|
|
179
|
+
const e = ++this.synthesisRunId, r = new AbortController();
|
|
176
180
|
this.activeSynthesisController = r;
|
|
177
|
-
const { audioUrl: i, wordTimings: o } = await this.synthesizeSpeech(
|
|
178
|
-
if (
|
|
181
|
+
const { audioUrl: i, wordTimings: o } = await this.synthesizeSpeech(t, r.signal, e);
|
|
182
|
+
if (e !== this.synthesisRunId) {
|
|
179
183
|
URL.revokeObjectURL(i);
|
|
180
184
|
return;
|
|
181
185
|
}
|
|
182
|
-
|
|
183
|
-
return this.wordTimings = o.map((a) => ({
|
|
184
|
-
...a,
|
|
185
|
-
time: a.time / s
|
|
186
|
-
})), new Promise((a, d) => {
|
|
186
|
+
return this.wordTimings = o, new Promise((s, a) => {
|
|
187
187
|
const n = new Audio(i);
|
|
188
|
-
this.currentAudio = n, this.config.
|
|
188
|
+
this.currentAudio = n, this.config.volume !== void 0 && (n.volume = Math.max(0, Math.min(1, this.config.volume))), n.onplay = () => {
|
|
189
189
|
this.pausedState = !1, this.onWordBoundary && this.wordTimings.length > 0 && this.startWordHighlighting();
|
|
190
190
|
}, n.onended = () => {
|
|
191
|
-
this.stopWordHighlighting(), URL.revokeObjectURL(i), this.currentAudio = null, this.wordTimings = [],
|
|
191
|
+
this.stopWordHighlighting(), URL.revokeObjectURL(i), this.currentAudio = null, this.wordTimings = [], s();
|
|
192
192
|
}, n.onerror = (h) => {
|
|
193
|
-
this.stopWordHighlighting(), URL.revokeObjectURL(i), this.currentAudio = null, this.wordTimings = [], this.intentionallyStopped ?
|
|
193
|
+
this.stopWordHighlighting(), URL.revokeObjectURL(i), this.currentAudio = null, this.wordTimings = [], this.intentionallyStopped ? s() : a(/* @__PURE__ */ new Error("Failed to play audio from server"));
|
|
194
194
|
}, n.onpause = () => {
|
|
195
195
|
this.stopWordHighlighting(), this.pausedState = !0;
|
|
196
|
-
}, n.play().catch(
|
|
196
|
+
}, n.play().catch(a);
|
|
197
197
|
});
|
|
198
198
|
}
|
|
199
|
-
async synthesizeSpeech(
|
|
199
|
+
async synthesizeSpeech(t, e, r) {
|
|
200
200
|
const i = Date.now();
|
|
201
201
|
await this.emitTelemetry("pie-tool-backend-call-start", {
|
|
202
202
|
toolId: "tts",
|
|
@@ -208,13 +208,13 @@ var R = (e) => {
|
|
|
208
208
|
...this.config.headers
|
|
209
209
|
};
|
|
210
210
|
this.config.authToken && (o.Authorization = `Bearer ${this.config.authToken}`);
|
|
211
|
-
const s = this.adapter.resolveSynthesisUrl(this.config), a = this.adapter.buildRequestBody(
|
|
211
|
+
const s = this.adapter.resolveSynthesisUrl(this.config), a = this.adapter.buildRequestBody(t, this.config), n = await (async () => {
|
|
212
212
|
try {
|
|
213
213
|
return await fetch(s, {
|
|
214
214
|
method: "POST",
|
|
215
215
|
headers: o,
|
|
216
216
|
body: JSON.stringify(a),
|
|
217
|
-
signal:
|
|
217
|
+
signal: e
|
|
218
218
|
});
|
|
219
219
|
} catch (c) {
|
|
220
220
|
throw await this.emitTelemetry("pie-tool-backend-call-error", {
|
|
@@ -227,77 +227,99 @@ var R = (e) => {
|
|
|
227
227
|
}), c;
|
|
228
228
|
}
|
|
229
229
|
})();
|
|
230
|
-
if (!
|
|
231
|
-
const c = await
|
|
232
|
-
|
|
230
|
+
if (!n.ok) {
|
|
231
|
+
const c = await n.text().catch(() => "");
|
|
232
|
+
let p = {};
|
|
233
|
+
if (c) try {
|
|
234
|
+
p = JSON.parse(c);
|
|
235
|
+
} catch (v) {
|
|
236
|
+
console.warn("[ServerTTSProvider] non-OK response body was not JSON", {
|
|
237
|
+
status: n.status,
|
|
238
|
+
url: s,
|
|
239
|
+
rawBodyPreview: c.slice(0, 500),
|
|
240
|
+
parseError: v instanceof Error ? v.message : String(v)
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
const g = typeof p.message == "string" && p.message || (typeof p.error?.message == "string" ? p.error.message : void 0) || `Server returned ${n.status}`;
|
|
244
|
+
await this.emitTelemetry("pie-tool-backend-call-error", {
|
|
233
245
|
toolId: "tts",
|
|
234
246
|
backend: this.config.provider || "server",
|
|
235
247
|
operation: "synthesize-speech",
|
|
236
248
|
duration: Date.now() - i,
|
|
237
|
-
statusCode:
|
|
249
|
+
statusCode: n.status,
|
|
238
250
|
errorType: "TTSBackendRequestError",
|
|
239
|
-
message:
|
|
240
|
-
})
|
|
251
|
+
message: g
|
|
252
|
+
});
|
|
253
|
+
const m = t.replace(/\s+/g, " ").trim().slice(0, 120);
|
|
254
|
+
throw console.error("[ServerTTSProvider] synthesize request failed", {
|
|
255
|
+
status: n.status,
|
|
256
|
+
url: s,
|
|
257
|
+
backend: this.config.provider || "server",
|
|
258
|
+
message: g,
|
|
259
|
+
responseBody: p,
|
|
260
|
+
textLength: t.length,
|
|
261
|
+
textPreview: m.length < t.length ? `${m}…` : m
|
|
262
|
+
}), new Error(g);
|
|
241
263
|
}
|
|
242
|
-
const
|
|
243
|
-
if (r !== this.synthesisRunId ||
|
|
244
|
-
let
|
|
245
|
-
if (
|
|
264
|
+
const h = await this.adapter.parseResponse(n, this.config, o, e);
|
|
265
|
+
if (r !== this.synthesisRunId || e.aborted) throw new Error("Synthesis superseded by a newer request");
|
|
266
|
+
let l;
|
|
267
|
+
if (h.audio.kind === "base64") l = this.base64ToBlob(h.audio.data, h.audio.contentType);
|
|
246
268
|
else {
|
|
247
|
-
const c =
|
|
269
|
+
const c = h.audio.url, p = Date.now();
|
|
248
270
|
await this.emitTelemetry("pie-tool-backend-call-start", {
|
|
249
271
|
toolId: "tts",
|
|
250
272
|
backend: this.config.provider || "server",
|
|
251
273
|
operation: "fetch-synthesized-audio-asset"
|
|
252
274
|
});
|
|
253
|
-
const
|
|
254
|
-
this.config.includeAuthOnAssetFetch && this.config.authToken && (
|
|
255
|
-
const
|
|
256
|
-
if (
|
|
275
|
+
const g = {};
|
|
276
|
+
this.config.includeAuthOnAssetFetch && this.config.authToken && (g.Authorization = `Bearer ${this.config.authToken}`);
|
|
277
|
+
const m = T(c, this.config);
|
|
278
|
+
if (m === null)
|
|
257
279
|
throw await this.emitTelemetry("pie-tool-backend-call-error", {
|
|
258
280
|
toolId: "tts",
|
|
259
281
|
backend: this.config.provider || "server",
|
|
260
282
|
operation: "fetch-synthesized-audio-asset",
|
|
261
|
-
duration: Date.now() -
|
|
283
|
+
duration: Date.now() - p,
|
|
262
284
|
errorType: "TTSAssetInvalidUrl",
|
|
263
285
|
message: "TTS asset URL rejected (non-http(s) or malformed)"
|
|
264
286
|
}), new Error("TTS asset URL rejected (non-http(s) or malformed)");
|
|
265
|
-
const
|
|
287
|
+
const v = A(m, this.config) ? g : R(g), f = await (async () => {
|
|
266
288
|
try {
|
|
267
|
-
return await fetch(
|
|
268
|
-
headers:
|
|
269
|
-
signal:
|
|
289
|
+
return await fetch(m.toString(), {
|
|
290
|
+
headers: v,
|
|
291
|
+
signal: e,
|
|
270
292
|
redirect: "error"
|
|
271
293
|
});
|
|
272
|
-
} catch (
|
|
294
|
+
} catch (w) {
|
|
273
295
|
throw await this.emitTelemetry("pie-tool-backend-call-error", {
|
|
274
296
|
toolId: "tts",
|
|
275
297
|
backend: this.config.provider || "server",
|
|
276
298
|
operation: "fetch-synthesized-audio-asset",
|
|
277
|
-
duration: Date.now() -
|
|
299
|
+
duration: Date.now() - p,
|
|
278
300
|
errorType: "TTSAssetNetworkError",
|
|
279
|
-
message:
|
|
280
|
-
}),
|
|
301
|
+
message: w instanceof Error ? w.message : String(w)
|
|
302
|
+
}), w;
|
|
281
303
|
}
|
|
282
304
|
})();
|
|
283
|
-
if (!
|
|
305
|
+
if (!f.ok)
|
|
284
306
|
throw await this.emitTelemetry("pie-tool-backend-call-error", {
|
|
285
307
|
toolId: "tts",
|
|
286
308
|
backend: this.config.provider || "server",
|
|
287
309
|
operation: "fetch-synthesized-audio-asset",
|
|
288
|
-
duration: Date.now() -
|
|
289
|
-
statusCode:
|
|
310
|
+
duration: Date.now() - p,
|
|
311
|
+
statusCode: f.status,
|
|
290
312
|
errorType: "TTSAssetFetchError",
|
|
291
|
-
message: `Failed to download synthesized audio (${
|
|
292
|
-
}), new Error(`Failed to download synthesized audio (${
|
|
293
|
-
|
|
313
|
+
message: `Failed to download synthesized audio (${f.status})`
|
|
314
|
+
}), new Error(`Failed to download synthesized audio (${f.status})`);
|
|
315
|
+
l = await f.blob(), await this.emitTelemetry("pie-tool-backend-call-success", {
|
|
294
316
|
toolId: "tts",
|
|
295
317
|
backend: this.config.provider || "server",
|
|
296
318
|
operation: "fetch-synthesized-audio-asset",
|
|
297
|
-
duration: Date.now() -
|
|
319
|
+
duration: Date.now() - p
|
|
298
320
|
});
|
|
299
321
|
}
|
|
300
|
-
const u = URL.createObjectURL(
|
|
322
|
+
const u = URL.createObjectURL(l), y = this.parseSpeechMarks(h.speechMarks);
|
|
301
323
|
return await this.emitTelemetry("pie-tool-backend-call-success", {
|
|
302
324
|
toolId: "tts",
|
|
303
325
|
backend: this.config.provider || "server",
|
|
@@ -305,21 +327,22 @@ var R = (e) => {
|
|
|
305
327
|
duration: Date.now() - i
|
|
306
328
|
}), {
|
|
307
329
|
audioUrl: u,
|
|
308
|
-
wordTimings:
|
|
330
|
+
wordTimings: y
|
|
309
331
|
};
|
|
310
332
|
}
|
|
311
|
-
base64ToBlob(
|
|
312
|
-
const r = atob(
|
|
333
|
+
base64ToBlob(t, e) {
|
|
334
|
+
const r = atob(t), i = new Array(r.length);
|
|
313
335
|
for (let s = 0; s < r.length; s++) i[s] = r.charCodeAt(s);
|
|
314
336
|
const o = new Uint8Array(i);
|
|
315
|
-
return new Blob([o], { type:
|
|
337
|
+
return new Blob([o], { type: e });
|
|
316
338
|
}
|
|
317
|
-
parseSpeechMarks(
|
|
318
|
-
return
|
|
319
|
-
time:
|
|
339
|
+
parseSpeechMarks(t) {
|
|
340
|
+
return t.filter((e) => e.type === "word").map((e, r) => ({
|
|
341
|
+
time: e.time,
|
|
320
342
|
wordIndex: r,
|
|
321
|
-
charIndex:
|
|
322
|
-
length:
|
|
343
|
+
charIndex: e.start,
|
|
344
|
+
length: e.end - e.start,
|
|
345
|
+
word: e.value
|
|
323
346
|
}));
|
|
324
347
|
}
|
|
325
348
|
startWordHighlighting() {
|
|
@@ -332,17 +355,17 @@ var R = (e) => {
|
|
|
332
355
|
return;
|
|
333
356
|
}
|
|
334
357
|
console.log("[ServerTTSProvider] Starting word highlighting with", this.wordTimings.length, "word timings"), console.log("[ServerTTSProvider] Playback rate:", this.currentAudio.playbackRate), console.log("[ServerTTSProvider] First 3 timings:", this.wordTimings.slice(0, 3));
|
|
335
|
-
let
|
|
358
|
+
let t = -1;
|
|
336
359
|
this.highlightInterval = window.setInterval(() => {
|
|
337
360
|
if (!this.currentAudio) {
|
|
338
361
|
this.stopWordHighlighting();
|
|
339
362
|
return;
|
|
340
363
|
}
|
|
341
|
-
const
|
|
364
|
+
const e = this.currentAudio.currentTime * 1e3;
|
|
342
365
|
for (let r = 0; r < this.wordTimings.length; r++) {
|
|
343
366
|
const i = this.wordTimings[r];
|
|
344
|
-
if (
|
|
345
|
-
this.onWordBoundary && (console.log("[ServerTTSProvider] Highlighting word at charIndex:", i.charIndex, "length:", i.length, "time:", i.time, "currentTime:",
|
|
367
|
+
if (e >= i.time && r > t) {
|
|
368
|
+
this.onWordBoundary && (console.log("[ServerTTSProvider] Highlighting word at charIndex:", i.charIndex, "length:", i.length, "time:", i.time, "currentTime:", e), this.onWordBoundary(i.word, i.charIndex, i.length)), t = r;
|
|
346
369
|
break;
|
|
347
370
|
}
|
|
348
371
|
}
|
|
@@ -366,63 +389,63 @@ var R = (e) => {
|
|
|
366
389
|
isPaused() {
|
|
367
390
|
return this.pausedState;
|
|
368
391
|
}
|
|
369
|
-
updateSettings(
|
|
370
|
-
|
|
392
|
+
updateSettings(t) {
|
|
393
|
+
t.rate !== void 0 && (this.config.rate = t.rate), t.pitch !== void 0 && (this.config.pitch = t.pitch), t.voice !== void 0 && (this.config.voice = t.voice);
|
|
371
394
|
}
|
|
372
|
-
},
|
|
395
|
+
}, N = class {
|
|
373
396
|
constructor() {
|
|
374
|
-
|
|
397
|
+
d(this, "providerId", "server-tts"), d(this, "providerName", "Server TTS"), d(this, "version", "1.0.0"), d(this, "config", null), d(this, "adapter", null), d(this, "telemetryReporter", void 0);
|
|
375
398
|
}
|
|
376
|
-
async emitTelemetry(
|
|
399
|
+
async emitTelemetry(t, e) {
|
|
377
400
|
try {
|
|
378
|
-
await this.telemetryReporter?.(
|
|
401
|
+
await this.telemetryReporter?.(t, e);
|
|
379
402
|
} catch (r) {
|
|
380
403
|
console.warn("[ServerTTSProvider] telemetry callback failed:", r);
|
|
381
404
|
}
|
|
382
405
|
}
|
|
383
|
-
async initialize(
|
|
384
|
-
const
|
|
385
|
-
if (!
|
|
386
|
-
this.config =
|
|
387
|
-
const r = b(
|
|
388
|
-
if (this.adapter =
|
|
406
|
+
async initialize(t) {
|
|
407
|
+
const e = t;
|
|
408
|
+
if (!e.apiEndpoint) throw new Error("apiEndpoint is required for ServerTTSProvider");
|
|
409
|
+
this.config = e, this.telemetryReporter = I(e);
|
|
410
|
+
const r = b(e);
|
|
411
|
+
if (this.adapter = z[r], e.validateEndpoint) {
|
|
389
412
|
const i = Date.now();
|
|
390
413
|
if (await this.emitTelemetry("pie-tool-backend-call-start", {
|
|
391
414
|
toolId: "tts",
|
|
392
|
-
backend:
|
|
415
|
+
backend: e.provider || "server",
|
|
393
416
|
operation: "validate-endpoint"
|
|
394
417
|
}), !await this.testAPIAvailability())
|
|
395
418
|
throw await this.emitTelemetry("pie-tool-backend-call-error", {
|
|
396
419
|
toolId: "tts",
|
|
397
|
-
backend:
|
|
420
|
+
backend: e.provider || "server",
|
|
398
421
|
operation: "validate-endpoint",
|
|
399
422
|
duration: Date.now() - i,
|
|
400
423
|
errorType: "TTSEndpointValidationError",
|
|
401
|
-
message: `Server TTS API not available at ${
|
|
402
|
-
}), new Error(`Server TTS API not available at ${
|
|
424
|
+
message: `Server TTS API not available at ${e.apiEndpoint}`
|
|
425
|
+
}), new Error(`Server TTS API not available at ${e.apiEndpoint}`);
|
|
403
426
|
await this.emitTelemetry("pie-tool-backend-call-success", {
|
|
404
427
|
toolId: "tts",
|
|
405
|
-
backend:
|
|
428
|
+
backend: e.provider || "server",
|
|
406
429
|
operation: "validate-endpoint",
|
|
407
430
|
duration: Date.now() - i
|
|
408
431
|
});
|
|
409
432
|
}
|
|
410
|
-
return new
|
|
433
|
+
return new x(e, this.adapter);
|
|
411
434
|
}
|
|
412
435
|
async testAPIAvailability() {
|
|
413
436
|
if (!this.config || !this.adapter) return !1;
|
|
414
437
|
try {
|
|
415
|
-
const
|
|
416
|
-
this.config.authToken && (
|
|
417
|
-
const
|
|
438
|
+
const t = { ...this.config.headers };
|
|
439
|
+
this.config.authToken && (t.Authorization = `Bearer ${this.config.authToken}`);
|
|
440
|
+
const e = new AbortController(), r = setTimeout(() => e.abort(), 5e3), i = U(this.config, this.adapter.id);
|
|
418
441
|
if (i === "none")
|
|
419
442
|
return clearTimeout(r), !0;
|
|
420
|
-
const o = i === "voices" ?
|
|
443
|
+
const o = i === "voices" ? O(this.config) : this.adapter.resolveSynthesisUrl(this.config), s = i === "voices" ? "GET" : "OPTIONS";
|
|
421
444
|
try {
|
|
422
445
|
const a = await fetch(o, {
|
|
423
446
|
method: s,
|
|
424
|
-
headers:
|
|
425
|
-
signal:
|
|
447
|
+
headers: t,
|
|
448
|
+
signal: e.signal
|
|
426
449
|
});
|
|
427
450
|
return clearTimeout(r), a.ok || a.status === 405;
|
|
428
451
|
} catch {
|
|
@@ -432,8 +455,8 @@ var R = (e) => {
|
|
|
432
455
|
return !1;
|
|
433
456
|
}
|
|
434
457
|
}
|
|
435
|
-
supportsFeature(
|
|
436
|
-
switch (
|
|
458
|
+
supportsFeature(t) {
|
|
459
|
+
switch (t) {
|
|
437
460
|
case "pause":
|
|
438
461
|
case "resume":
|
|
439
462
|
case "wordBoundary":
|
|
@@ -447,6 +470,7 @@ var R = (e) => {
|
|
|
447
470
|
}
|
|
448
471
|
}
|
|
449
472
|
getCapabilities() {
|
|
473
|
+
const t = this.config ? b(this.config) : "pie";
|
|
450
474
|
return {
|
|
451
475
|
supportsPause: !0,
|
|
452
476
|
supportsResume: !0,
|
|
@@ -454,7 +478,8 @@ var R = (e) => {
|
|
|
454
478
|
supportsVoiceSelection: !0,
|
|
455
479
|
supportsRateControl: !0,
|
|
456
480
|
supportsPitchControl: !1,
|
|
457
|
-
|
|
481
|
+
supportsSSML: P(this.config),
|
|
482
|
+
maxTextLength: E[t]
|
|
458
483
|
};
|
|
459
484
|
}
|
|
460
485
|
destroy() {
|
|
@@ -462,5 +487,5 @@ var R = (e) => {
|
|
|
462
487
|
}
|
|
463
488
|
};
|
|
464
489
|
export {
|
|
465
|
-
|
|
490
|
+
N as ServerTTSProvider
|
|
466
491
|
};
|