@pie-players/pie-section-player 0.3.19 → 0.3.20

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,33 @@
1
+ function e(t) {
2
+ "@babel/helpers - typeof";
3
+ return e = typeof Symbol == "function" && typeof Symbol.iterator == "symbol" ? function(r) {
4
+ return typeof r;
5
+ } : function(r) {
6
+ return r && typeof Symbol == "function" && r.constructor === Symbol && r !== Symbol.prototype ? "symbol" : typeof r;
7
+ }, e(t);
8
+ }
9
+ function i(t, r) {
10
+ if (e(t) != "object" || !t) return t;
11
+ var o = t[Symbol.toPrimitive];
12
+ if (o !== void 0) {
13
+ var n = o.call(t, r || "default");
14
+ if (e(n) != "object") return n;
15
+ throw new TypeError("@@toPrimitive must return a primitive value.");
16
+ }
17
+ return (r === "string" ? String : Number)(t);
18
+ }
19
+ function u(t) {
20
+ var r = i(t, "string");
21
+ return e(r) == "symbol" ? r : r + "";
22
+ }
23
+ function f(t, r, o) {
24
+ return (r = u(r)) in t ? Object.defineProperty(t, r, {
25
+ value: o,
26
+ enumerable: !0,
27
+ configurable: !0,
28
+ writable: !0
29
+ }) : t[r] = o, t;
30
+ }
31
+ export {
32
+ f as t
33
+ };
@@ -0,0 +1,33 @@
1
+ function o(e) {
2
+ "@babel/helpers - typeof";
3
+ return o = typeof Symbol == "function" && typeof Symbol.iterator == "symbol" ? function(t) {
4
+ return typeof t;
5
+ } : function(t) {
6
+ return t && typeof Symbol == "function" && t.constructor === Symbol && t !== Symbol.prototype ? "symbol" : typeof t;
7
+ }, o(e);
8
+ }
9
+ function i(e, t) {
10
+ if (o(e) != "object" || !e) return e;
11
+ var r = e[Symbol.toPrimitive];
12
+ if (r !== void 0) {
13
+ var n = r.call(e, t || "default");
14
+ if (o(n) != "object") return n;
15
+ throw new TypeError("@@toPrimitive must return a primitive value.");
16
+ }
17
+ return (t === "string" ? String : Number)(e);
18
+ }
19
+ function u(e) {
20
+ var t = i(e, "string");
21
+ return o(t) == "symbol" ? t : t + "";
22
+ }
23
+ function f(e, t, r) {
24
+ return (t = u(t)) in e ? Object.defineProperty(e, t, {
25
+ value: r,
26
+ enumerable: !0,
27
+ configurable: !0,
28
+ writable: !0
29
+ }) : e[t] = r, e;
30
+ }
31
+ export {
32
+ f as t
33
+ };
@@ -0,0 +1,393 @@
1
+ import { t as h } from "./defineProperty-CyepwRr5.js";
2
+ var b = (t) => {
3
+ const e = (t.providerOptions && typeof t.providerOptions == "object" ? t.providerOptions : {}).__pieTelemetry;
4
+ return typeof e == "function" ? e : void 0;
5
+ }, S = {
6
+ pie: 3e3,
7
+ custom: 3e3
8
+ }, v = (t) => t.replace(/\/+$/, ""), k = (t) => {
9
+ const e = v(t.apiEndpoint), r = (t.provider || "").toLowerCase();
10
+ return r === "polly" || r === "google" ? `${e}/${r}/voices` : `${e}/voices`;
11
+ }, T = (t) => t.transportMode === "custom" ? "custom" : t.transportMode === "pie" ? "pie" : t.provider === "custom" ? "custom" : "pie", w = (t, e) => t.endpointMode ? t.endpointMode : e === "custom" ? "rootPost" : "synthesizePath", A = (t, e) => t.endpointValidationMode ? t.endpointValidationMode : e === "custom" ? "none" : "voices", R = (t) => {
12
+ const e = t.providerOptions || {};
13
+ if (typeof e.speedRate == "string") return e.speedRate;
14
+ const r = Number(t.rate ?? 1);
15
+ return !Number.isFinite(r) || r <= 0.95 ? "slow" : r >= 1.5 ? "fast" : "medium";
16
+ }, I = (t) => {
17
+ const e = [];
18
+ let r = 0;
19
+ const i = t.split(`
20
+ `).map((o) => o.trim()).filter(Boolean);
21
+ for (const o of i) try {
22
+ const s = JSON.parse(o), d = typeof s.type == "string" ? s.type : "word", n = typeof s.time == "number" && Number.isFinite(s.time) ? s.time : 0, a = typeof s.value == "string" ? s.value : "", c = typeof s.start == "number" && Number.isFinite(s.start) ? s.start : null, y = typeof s.end == "number" && Number.isFinite(s.end) ? s.end : null, g = c ?? r, l = y ?? g + Math.max(1, a.length || String(s.value || "").length);
23
+ r = Math.max(l + 1, r), e.push({
24
+ time: n,
25
+ type: d,
26
+ start: g,
27
+ end: l,
28
+ value: a
29
+ });
30
+ } catch {
31
+ }
32
+ return e;
33
+ }, M = {
34
+ pie: {
35
+ id: "pie",
36
+ resolveSynthesisUrl: (t) => {
37
+ const e = w(t, "pie"), r = v(t.apiEndpoint);
38
+ return e === "rootPost" ? r : `${r}/synthesize`;
39
+ },
40
+ buildRequestBody: (t, e) => {
41
+ 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, d = Array.isArray(r.speechMarkTypes) ? r.speechMarkTypes.filter((n) => n === "word" || n === "sentence" || n === "ssml") : void 0;
42
+ return {
43
+ text: t,
44
+ provider: e.provider || "polly",
45
+ voice: e.voice,
46
+ language: e.language,
47
+ rate: e.rate,
48
+ engine: i,
49
+ sampleRate: o,
50
+ format: s,
51
+ speechMarkTypes: d,
52
+ includeSpeechMarks: !0
53
+ };
54
+ },
55
+ parseResponse: async (t) => {
56
+ const e = await t.json();
57
+ return {
58
+ audio: {
59
+ kind: "base64",
60
+ data: e.audio,
61
+ contentType: e.contentType
62
+ },
63
+ speechMarks: Array.isArray(e.speechMarks) ? e.speechMarks : []
64
+ };
65
+ }
66
+ },
67
+ custom: {
68
+ id: "custom",
69
+ resolveSynthesisUrl: (t) => {
70
+ const e = w(t, "custom"), r = v(t.apiEndpoint);
71
+ return e === "synthesizePath" ? `${r}/synthesize` : r;
72
+ },
73
+ buildRequestBody: (t, e) => {
74
+ 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;
75
+ return {
76
+ text: t,
77
+ speedRate: R(e),
78
+ lang_id: i,
79
+ cache: o
80
+ };
81
+ },
82
+ parseResponse: async (t, e, r, i) => {
83
+ const o = await t.json(), s = {};
84
+ if (e.includeAuthOnAssetFetch)
85
+ for (const [n, a] of Object.entries(r)) n.toLowerCase() === "authorization" && (s[n] = a);
86
+ let d = [];
87
+ if (typeof o.word == "string" && o.word.length > 0) {
88
+ const n = await fetch(o.word, {
89
+ headers: s,
90
+ signal: i
91
+ });
92
+ n.ok && (d = I(await n.text()));
93
+ }
94
+ return {
95
+ audio: {
96
+ kind: "url",
97
+ url: o.audioContent
98
+ },
99
+ speechMarks: d
100
+ };
101
+ }
102
+ }
103
+ }, E = class {
104
+ constructor(t, e) {
105
+ h(this, "config", void 0), h(this, "adapter", void 0), h(this, "currentAudio", null), h(this, "pausedState", !1), h(this, "wordTimings", []), h(this, "highlightInterval", null), h(this, "intentionallyStopped", !1), h(this, "activeSynthesisController", null), h(this, "synthesisRunId", 0), h(this, "telemetryReporter", void 0), h(this, "onWordBoundary", void 0), this.config = t, this.adapter = e, this.telemetryReporter = b(t);
106
+ }
107
+ async emitTelemetry(t, e) {
108
+ try {
109
+ await this.telemetryReporter?.(t, e);
110
+ } catch (r) {
111
+ console.warn("[ServerTTSProvider] telemetry callback failed:", r);
112
+ }
113
+ }
114
+ async speak(t) {
115
+ this.stop(), this.intentionallyStopped = !1;
116
+ const e = ++this.synthesisRunId, r = new AbortController();
117
+ this.activeSynthesisController = r;
118
+ const { audioUrl: i, wordTimings: o } = await this.synthesizeSpeech(t, r.signal, e);
119
+ if (e !== this.synthesisRunId) {
120
+ URL.revokeObjectURL(i);
121
+ return;
122
+ }
123
+ const s = this.config.rate || 1;
124
+ return this.wordTimings = o.map((d) => ({
125
+ ...d,
126
+ time: d.time / s
127
+ })), new Promise((d, n) => {
128
+ const a = new Audio(i);
129
+ this.currentAudio = a, this.config.rate && (a.playbackRate = Math.max(0.25, Math.min(4, this.config.rate))), this.config.volume !== void 0 && (a.volume = Math.max(0, Math.min(1, this.config.volume))), a.onplay = () => {
130
+ this.pausedState = !1, this.onWordBoundary && this.wordTimings.length > 0 && this.startWordHighlighting();
131
+ }, a.onended = () => {
132
+ this.stopWordHighlighting(), URL.revokeObjectURL(i), this.currentAudio = null, this.wordTimings = [], d();
133
+ }, a.onerror = (c) => {
134
+ this.stopWordHighlighting(), URL.revokeObjectURL(i), this.currentAudio = null, this.wordTimings = [], this.intentionallyStopped ? d() : n(/* @__PURE__ */ new Error("Failed to play audio from server"));
135
+ }, a.onpause = () => {
136
+ this.stopWordHighlighting(), this.pausedState = !0;
137
+ }, a.play().catch(n);
138
+ });
139
+ }
140
+ async synthesizeSpeech(t, e, r) {
141
+ const i = Date.now();
142
+ await this.emitTelemetry("pie-tool-backend-call-start", {
143
+ toolId: "tts",
144
+ backend: this.config.provider || "server",
145
+ operation: "synthesize-speech"
146
+ });
147
+ const o = {
148
+ "Content-Type": "application/json",
149
+ ...this.config.headers
150
+ };
151
+ this.config.authToken && (o.Authorization = `Bearer ${this.config.authToken}`);
152
+ const s = this.adapter.resolveSynthesisUrl(this.config), d = this.adapter.buildRequestBody(t, this.config), n = await (async () => {
153
+ try {
154
+ return await fetch(s, {
155
+ method: "POST",
156
+ headers: o,
157
+ body: JSON.stringify(d),
158
+ signal: e
159
+ });
160
+ } catch (l) {
161
+ throw await this.emitTelemetry("pie-tool-backend-call-error", {
162
+ toolId: "tts",
163
+ backend: this.config.provider || "server",
164
+ operation: "synthesize-speech",
165
+ duration: Date.now() - i,
166
+ errorType: "TTSBackendNetworkError",
167
+ message: l instanceof Error ? l.message : String(l)
168
+ }), l;
169
+ }
170
+ })();
171
+ if (!n.ok) {
172
+ const l = await n.json().catch(() => ({})), u = l.message || l.error?.message || `Server returned ${n.status}`;
173
+ throw await this.emitTelemetry("pie-tool-backend-call-error", {
174
+ toolId: "tts",
175
+ backend: this.config.provider || "server",
176
+ operation: "synthesize-speech",
177
+ duration: Date.now() - i,
178
+ statusCode: n.status,
179
+ errorType: "TTSBackendRequestError",
180
+ message: u
181
+ }), new Error(u);
182
+ }
183
+ const a = await this.adapter.parseResponse(n, this.config, o, e);
184
+ if (r !== this.synthesisRunId || e.aborted) throw new Error("Synthesis superseded by a newer request");
185
+ let c;
186
+ if (a.audio.kind === "base64") c = this.base64ToBlob(a.audio.data, a.audio.contentType);
187
+ else {
188
+ const l = a.audio.url, u = Date.now();
189
+ await this.emitTelemetry("pie-tool-backend-call-start", {
190
+ toolId: "tts",
191
+ backend: this.config.provider || "server",
192
+ operation: "fetch-synthesized-audio-asset"
193
+ });
194
+ const f = {};
195
+ this.config.includeAuthOnAssetFetch && this.config.authToken && (f.Authorization = `Bearer ${this.config.authToken}`);
196
+ const p = await (async () => {
197
+ try {
198
+ return await fetch(l, {
199
+ headers: f,
200
+ signal: e
201
+ });
202
+ } catch (m) {
203
+ throw await this.emitTelemetry("pie-tool-backend-call-error", {
204
+ toolId: "tts",
205
+ backend: this.config.provider || "server",
206
+ operation: "fetch-synthesized-audio-asset",
207
+ duration: Date.now() - u,
208
+ errorType: "TTSAssetNetworkError",
209
+ message: m instanceof Error ? m.message : String(m)
210
+ }), m;
211
+ }
212
+ })();
213
+ if (!p.ok)
214
+ throw await this.emitTelemetry("pie-tool-backend-call-error", {
215
+ toolId: "tts",
216
+ backend: this.config.provider || "server",
217
+ operation: "fetch-synthesized-audio-asset",
218
+ duration: Date.now() - u,
219
+ statusCode: p.status,
220
+ errorType: "TTSAssetFetchError",
221
+ message: `Failed to download synthesized audio (${p.status})`
222
+ }), new Error(`Failed to download synthesized audio (${p.status})`);
223
+ c = await p.blob(), await this.emitTelemetry("pie-tool-backend-call-success", {
224
+ toolId: "tts",
225
+ backend: this.config.provider || "server",
226
+ operation: "fetch-synthesized-audio-asset",
227
+ duration: Date.now() - u
228
+ });
229
+ }
230
+ const y = URL.createObjectURL(c), g = this.parseSpeechMarks(a.speechMarks);
231
+ return await this.emitTelemetry("pie-tool-backend-call-success", {
232
+ toolId: "tts",
233
+ backend: this.config.provider || "server",
234
+ operation: "synthesize-speech",
235
+ duration: Date.now() - i
236
+ }), {
237
+ audioUrl: y,
238
+ wordTimings: g
239
+ };
240
+ }
241
+ base64ToBlob(t, e) {
242
+ const r = atob(t), i = new Array(r.length);
243
+ for (let o = 0; o < r.length; o++) i[o] = r.charCodeAt(o);
244
+ return new Blob([new Uint8Array(i)], { type: e });
245
+ }
246
+ parseSpeechMarks(t) {
247
+ return t.filter((e) => e.type === "word").map((e, r) => ({
248
+ time: e.time,
249
+ wordIndex: r,
250
+ charIndex: e.start,
251
+ length: e.end - e.start
252
+ }));
253
+ }
254
+ startWordHighlighting() {
255
+ if (this.stopWordHighlighting(), !this.currentAudio || !this.onWordBoundary || this.wordTimings.length === 0) {
256
+ console.log("[ServerTTSProvider] Cannot start highlighting:", {
257
+ hasAudio: !!this.currentAudio,
258
+ hasCallback: !!this.onWordBoundary,
259
+ wordTimingsCount: this.wordTimings.length
260
+ });
261
+ return;
262
+ }
263
+ 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));
264
+ let t = -1;
265
+ this.highlightInterval = window.setInterval(() => {
266
+ if (!this.currentAudio) {
267
+ this.stopWordHighlighting();
268
+ return;
269
+ }
270
+ const e = this.currentAudio.currentTime * 1e3;
271
+ for (let r = 0; r < this.wordTimings.length; r++) {
272
+ const i = this.wordTimings[r];
273
+ if (e >= i.time && r > t) {
274
+ this.onWordBoundary && (console.log("[ServerTTSProvider] Highlighting word at charIndex:", i.charIndex, "length:", i.length, "time:", i.time, "currentTime:", e), this.onWordBoundary("", i.charIndex, i.length)), t = r;
275
+ break;
276
+ }
277
+ }
278
+ }, 50);
279
+ }
280
+ stopWordHighlighting() {
281
+ this.highlightInterval !== null && (clearInterval(this.highlightInterval), this.highlightInterval = null);
282
+ }
283
+ pause() {
284
+ this.currentAudio && !this.pausedState && (this.currentAudio.pause(), this.stopWordHighlighting(), this.pausedState = !0);
285
+ }
286
+ resume() {
287
+ this.currentAudio && this.pausedState && (this.currentAudio.play(), this.pausedState = !1, this.onWordBoundary && this.wordTimings.length > 0 && this.startWordHighlighting());
288
+ }
289
+ stop() {
290
+ this.synthesisRunId += 1, this.activeSynthesisController && (this.activeSynthesisController.abort(), this.activeSynthesisController = null), this.stopWordHighlighting(), this.currentAudio && (this.intentionallyStopped = !0, this.currentAudio.pause(), this.currentAudio.src && URL.revokeObjectURL(this.currentAudio.src), this.currentAudio.src = "", this.currentAudio = null), this.pausedState = !1, this.wordTimings = [];
291
+ }
292
+ isPlaying() {
293
+ return this.currentAudio !== null && !this.pausedState;
294
+ }
295
+ isPaused() {
296
+ return this.pausedState;
297
+ }
298
+ updateSettings(t) {
299
+ t.rate !== void 0 && (this.config.rate = t.rate, this.currentAudio && (this.currentAudio.playbackRate = Math.max(0.25, Math.min(4, t.rate)))), t.pitch !== void 0 && (this.config.pitch = t.pitch), t.voice !== void 0 && (this.config.voice = t.voice);
300
+ }
301
+ }, C = class {
302
+ constructor() {
303
+ h(this, "providerId", "server-tts"), h(this, "providerName", "Server TTS"), h(this, "version", "1.0.0"), h(this, "config", null), h(this, "adapter", null), h(this, "telemetryReporter", void 0);
304
+ }
305
+ async emitTelemetry(t, e) {
306
+ try {
307
+ await this.telemetryReporter?.(t, e);
308
+ } catch (r) {
309
+ console.warn("[ServerTTSProvider] telemetry callback failed:", r);
310
+ }
311
+ }
312
+ async initialize(t) {
313
+ const e = t;
314
+ if (!e.apiEndpoint) throw new Error("apiEndpoint is required for ServerTTSProvider");
315
+ if (this.config = e, this.telemetryReporter = b(e), this.adapter = M[T(e)], e.validateEndpoint) {
316
+ const r = Date.now();
317
+ if (await this.emitTelemetry("pie-tool-backend-call-start", {
318
+ toolId: "tts",
319
+ backend: e.provider || "server",
320
+ operation: "validate-endpoint"
321
+ }), !await this.testAPIAvailability())
322
+ throw await this.emitTelemetry("pie-tool-backend-call-error", {
323
+ toolId: "tts",
324
+ backend: e.provider || "server",
325
+ operation: "validate-endpoint",
326
+ duration: Date.now() - r,
327
+ errorType: "TTSEndpointValidationError",
328
+ message: `Server TTS API not available at ${e.apiEndpoint}`
329
+ }), new Error(`Server TTS API not available at ${e.apiEndpoint}`);
330
+ await this.emitTelemetry("pie-tool-backend-call-success", {
331
+ toolId: "tts",
332
+ backend: e.provider || "server",
333
+ operation: "validate-endpoint",
334
+ duration: Date.now() - r
335
+ });
336
+ }
337
+ return new E(e, this.adapter);
338
+ }
339
+ async testAPIAvailability() {
340
+ if (!this.config || !this.adapter) return !1;
341
+ try {
342
+ const t = { ...this.config.headers };
343
+ this.config.authToken && (t.Authorization = `Bearer ${this.config.authToken}`);
344
+ const e = new AbortController(), r = setTimeout(() => e.abort(), 5e3), i = A(this.config, this.adapter.id);
345
+ if (i === "none")
346
+ return clearTimeout(r), !0;
347
+ const o = i === "voices" ? k(this.config) : this.adapter.resolveSynthesisUrl(this.config), s = i === "voices" ? "GET" : "OPTIONS";
348
+ try {
349
+ const d = await fetch(o, {
350
+ method: s,
351
+ headers: t,
352
+ signal: e.signal
353
+ });
354
+ return clearTimeout(r), d.ok || d.status === 405;
355
+ } catch {
356
+ return clearTimeout(r), !1;
357
+ }
358
+ } catch {
359
+ return !1;
360
+ }
361
+ }
362
+ supportsFeature(t) {
363
+ switch (t) {
364
+ case "pause":
365
+ case "resume":
366
+ case "wordBoundary":
367
+ case "voiceSelection":
368
+ case "rateControl":
369
+ return !0;
370
+ case "pitchControl":
371
+ return !1;
372
+ default:
373
+ return !1;
374
+ }
375
+ }
376
+ getCapabilities() {
377
+ return {
378
+ supportsPause: !0,
379
+ supportsResume: !0,
380
+ supportsWordBoundary: !0,
381
+ supportsVoiceSelection: !0,
382
+ supportsRateControl: !0,
383
+ supportsPitchControl: !1,
384
+ maxTextLength: S[this.config ? T(this.config) : "pie"]
385
+ };
386
+ }
387
+ destroy() {
388
+ this.config = null, this.adapter = null, this.telemetryReporter = void 0;
389
+ }
390
+ };
391
+ export {
392
+ C as ServerTTSProvider
393
+ };