speechflow 1.6.1 → 1.6.3
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/CHANGELOG.md +11 -0
- package/etc/claude.md +9 -4
- package/etc/keyfiles.txt +19 -0
- package/etc/speechflow.yaml +4 -4
- package/package.json +3 -3
- package/speechflow-cli/dst/speechflow-main-graph.js +2 -4
- package/speechflow-cli/dst/speechflow-main-graph.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-gender.js +16 -5
- package/speechflow-cli/dst/speechflow-node-a2a-gender.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2t-deepgram.js +4 -1
- package/speechflow-cli/dst/speechflow-node-a2t-deepgram.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-xio-device.d.ts +3 -0
- package/speechflow-cli/dst/speechflow-node-xio-device.js +99 -84
- package/speechflow-cli/dst/speechflow-node-xio-device.js.map +1 -1
- package/speechflow-cli/dst/speechflow-util-error.d.ts +1 -0
- package/speechflow-cli/dst/speechflow-util-error.js +7 -0
- package/speechflow-cli/dst/speechflow-util-error.js.map +1 -1
- package/speechflow-cli/src/lib.d.ts +2 -0
- package/speechflow-cli/src/speechflow-main-graph.ts +2 -4
- package/speechflow-cli/src/speechflow-node-a2a-gender.ts +3 -2
- package/speechflow-cli/src/speechflow-node-a2t-deepgram.ts +4 -1
- package/speechflow-cli/src/speechflow-node-xio-device.ts +108 -89
- package/speechflow-cli/src/speechflow-util-error.ts +7 -0
- package/speechflow-ui-db/dst/index.js +1 -1
- package/speechflow-ui-db/etc/vite-client.mts +0 -2
- package/speechflow-ui-db/src/app.vue +20 -19
- package/speechflow-ui-st/dst/index.js +1 -1
- package/speechflow-ui-st/etc/vite-client.mts +0 -2
- package/speechflow-ui-st/src/app.vue +10 -6
|
@@ -5,10 +5,17 @@
|
|
|
5
5
|
** Licensed under GPL 3.0 <https://spdx.org/licenses/GPL-3.0-only>
|
|
6
6
|
*/
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.timeoutPromise = timeoutPromise;
|
|
8
9
|
exports.ensureError = ensureError;
|
|
9
10
|
exports.ensurePromise = ensurePromise;
|
|
10
11
|
exports.run = run;
|
|
11
12
|
exports.runner = runner;
|
|
13
|
+
/* helper function for promise-based timeout */
|
|
14
|
+
function timeoutPromise(duration = 10 * 1000, info = "timeout") {
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
setTimeout(() => { reject(new Error(info)); }, duration);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
12
19
|
/* helper function for retrieving an Error object */
|
|
13
20
|
function ensureError(error, prefix, debug = false) {
|
|
14
21
|
if (error instanceof Error && prefix === undefined && debug === false)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"speechflow-util-error.js","sourceRoot":"","sources":["../src/speechflow-util-error.ts"],"names":[],"mappings":";AAAA;;;;EAIE;;AAGF,kCAUC;AAGD,sCAIC;AAqCD,kBAoEC;AA0BD,wBA4BC;
|
|
1
|
+
{"version":3,"file":"speechflow-util-error.js","sourceRoot":"","sources":["../src/speechflow-util-error.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
** Licensed under GPL 3.0 <https://spdx.org/licenses/GPL-3.0-only>
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
/* type definitions for Stream.compose */
|
|
7
8
|
declare module "node:stream" {
|
|
8
9
|
import { Stream, Duplex } from "node:stream"
|
|
9
10
|
export function compose (...streams: Stream[]): Duplex
|
|
@@ -33,3 +34,4 @@ declare function registerProcessor(
|
|
|
33
34
|
name: string,
|
|
34
35
|
processorCtor: new (...args: any[]) => AudioWorkletProcessor
|
|
35
36
|
): void
|
|
37
|
+
|
|
@@ -337,11 +337,9 @@ export class NodeGraph {
|
|
|
337
337
|
if (this.shuttingDown)
|
|
338
338
|
return
|
|
339
339
|
this.shuttingDown = true
|
|
340
|
-
if (signal === "
|
|
341
|
-
this.cli.log("info", "**** streams of all nodes finished -- shutting down service ****")
|
|
342
|
-
else if (signal === "exception")
|
|
340
|
+
if (signal === "exception")
|
|
343
341
|
this.cli.log("warning", "**** exception occurred -- shutting down service ****")
|
|
344
|
-
else
|
|
342
|
+
else if (signal !== "finished")
|
|
345
343
|
this.cli.log("warning", `**** received signal ${signal} -- shutting down service ****`)
|
|
346
344
|
|
|
347
345
|
/* shutdown API service */
|
|
@@ -149,9 +149,9 @@ export default class SpeechFlowNodeA2AGender extends SpeechFlowNode {
|
|
|
149
149
|
const c2 = classified.find((c: any) => c.label === "female")
|
|
150
150
|
const male = c1 ? c1.score : 0.0
|
|
151
151
|
const female = c2 ? c2.score : 0.0
|
|
152
|
-
if (male > female)
|
|
152
|
+
if (male > 0.50 && male > female + 0.25)
|
|
153
153
|
return "male"
|
|
154
|
-
else if (male
|
|
154
|
+
else if (female > 0.50 && female > male + 0.25)
|
|
155
155
|
return "female"
|
|
156
156
|
else
|
|
157
157
|
return "unknown"
|
|
@@ -176,6 +176,7 @@ export default class SpeechFlowNodeA2AGender extends SpeechFlowNode {
|
|
|
176
176
|
}
|
|
177
177
|
this.queue.off("write", workOffQueue)
|
|
178
178
|
|
|
179
|
+
/* workoff the queue */
|
|
179
180
|
try {
|
|
180
181
|
let pos0 = this.queueAC.position()
|
|
181
182
|
const posL = this.queueAC.maxPosition()
|
|
@@ -123,7 +123,10 @@ export default class SpeechFlowNodeA2TDeepgram extends SpeechFlowNode {
|
|
|
123
123
|
if (text === "")
|
|
124
124
|
this.log("info", `empty/dummy text received (start: ${data.start}s, duration: ${data.duration.toFixed(2)}s)`)
|
|
125
125
|
else {
|
|
126
|
-
this.log("info", `text received (start: ${data.start}s,
|
|
126
|
+
this.log("info", `text received (start: ${data.start}s, ` +
|
|
127
|
+
`duration: ${data.duration.toFixed(2)}s, ` +
|
|
128
|
+
`kind: ${isFinal ? "final" : "intermediate"}): ` +
|
|
129
|
+
`${text}"`)
|
|
127
130
|
const start = Duration.fromMillis(data.start * 1000).plus(this.timeZeroOffset)
|
|
128
131
|
const end = start.plus({ seconds: data.duration })
|
|
129
132
|
const metas = metastore.fetch(start, end)
|
|
@@ -83,6 +83,84 @@ export default class SpeechFlowNodeXIODevice extends SpeechFlowNode {
|
|
|
83
83
|
return device
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
/* NOTICE: "naudion" actually implements Stream.{Readable,Writable,Duplex}, but
|
|
87
|
+
declares just its sub-interface NodeJS.{Readable,Writable,Duplex}Stream,
|
|
88
|
+
so it is correct to cast it back to Stream.{Readable,Writable,Duplex}
|
|
89
|
+
in the following device stream setup functions! */
|
|
90
|
+
|
|
91
|
+
/* INTERNAL: setup duplex stream */
|
|
92
|
+
private setupDuplexStream (device: PortAudio.DeviceInfo, highwaterMark: number) {
|
|
93
|
+
if (device.maxInputChannels === 0)
|
|
94
|
+
throw new Error(`device "${device.id}" does not have any input channels (required by read/write mode)`)
|
|
95
|
+
if (device.maxOutputChannels === 0)
|
|
96
|
+
throw new Error(`device "${device.id}" does not have any output channels (required by read/write mode)`)
|
|
97
|
+
this.log("info", `resolved "${this.params.device}" to duplex device "${device.id}"`)
|
|
98
|
+
this.io = PortAudio.AudioIO({
|
|
99
|
+
inOptions: {
|
|
100
|
+
deviceId: device.id,
|
|
101
|
+
channelCount: this.config.audioChannels,
|
|
102
|
+
sampleRate: this.config.audioSampleRate,
|
|
103
|
+
sampleFormat: this.config.audioBitDepth,
|
|
104
|
+
highwaterMark
|
|
105
|
+
},
|
|
106
|
+
outOptions: {
|
|
107
|
+
deviceId: device.id,
|
|
108
|
+
channelCount: this.config.audioChannels,
|
|
109
|
+
sampleRate: this.config.audioSampleRate,
|
|
110
|
+
sampleFormat: this.config.audioBitDepth,
|
|
111
|
+
highwaterMark
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
this.stream = this.io as unknown as Stream.Duplex
|
|
115
|
+
|
|
116
|
+
/* convert regular stream into object-mode stream */
|
|
117
|
+
const wrapper1 = util.createTransformStreamForWritableSide()
|
|
118
|
+
const wrapper2 = util.createTransformStreamForReadableSide("audio", () => this.timeZero)
|
|
119
|
+
this.stream = Stream.compose(wrapper1, this.stream, wrapper2)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* INTERNAL: setup input stream */
|
|
123
|
+
private setupInputStream (device: PortAudio.DeviceInfo, highwaterMark: number) {
|
|
124
|
+
if (device.maxInputChannels === 0)
|
|
125
|
+
throw new Error(`device "${device.id}" does not have any input channels (required by read mode)`)
|
|
126
|
+
this.log("info", `resolved "${this.params.device}" to input device "${device.id}"`)
|
|
127
|
+
this.io = PortAudio.AudioIO({
|
|
128
|
+
inOptions: {
|
|
129
|
+
deviceId: device.id,
|
|
130
|
+
channelCount: this.config.audioChannels,
|
|
131
|
+
sampleRate: this.config.audioSampleRate,
|
|
132
|
+
sampleFormat: this.config.audioBitDepth,
|
|
133
|
+
highwaterMark
|
|
134
|
+
}
|
|
135
|
+
})
|
|
136
|
+
this.stream = this.io as unknown as Stream.Readable
|
|
137
|
+
|
|
138
|
+
/* convert regular stream into object-mode stream */
|
|
139
|
+
const wrapper = util.createTransformStreamForReadableSide("audio", () => this.timeZero)
|
|
140
|
+
this.stream = Stream.compose(this.stream, wrapper)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/* INTERNAL: setup output stream */
|
|
144
|
+
private setupOutputStream (device: PortAudio.DeviceInfo, highwaterMark: number) {
|
|
145
|
+
if (device.maxOutputChannels === 0)
|
|
146
|
+
throw new Error(`device "${device.id}" does not have any output channels (required by write mode)`)
|
|
147
|
+
this.log("info", `resolved "${this.params.device}" to output device "${device.id}"`)
|
|
148
|
+
this.io = PortAudio.AudioIO({
|
|
149
|
+
outOptions: {
|
|
150
|
+
deviceId: device.id,
|
|
151
|
+
channelCount: this.config.audioChannels,
|
|
152
|
+
sampleRate: this.config.audioSampleRate,
|
|
153
|
+
sampleFormat: this.config.audioBitDepth,
|
|
154
|
+
highwaterMark
|
|
155
|
+
}
|
|
156
|
+
})
|
|
157
|
+
this.stream = this.io as unknown as Stream.Writable
|
|
158
|
+
|
|
159
|
+
/* convert regular stream into object-mode stream */
|
|
160
|
+
const wrapper = util.createTransformStreamForWritableSide()
|
|
161
|
+
this.stream = Stream.compose(wrapper, this.stream)
|
|
162
|
+
}
|
|
163
|
+
|
|
86
164
|
/* open node */
|
|
87
165
|
async open () {
|
|
88
166
|
if (this.params.device === "")
|
|
@@ -104,107 +182,48 @@ export default class SpeechFlowNodeXIODevice extends SpeechFlowNode {
|
|
|
104
182
|
(this.config.audioBitDepth / 8)
|
|
105
183
|
) / (1000 / this.params.chunk)
|
|
106
184
|
|
|
107
|
-
/* establish device
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (this.params.mode === "
|
|
113
|
-
|
|
114
|
-
if (device.maxInputChannels === 0)
|
|
115
|
-
throw new Error(`device "${device.id}" does not have any input channels (required by read/write mode)`)
|
|
116
|
-
if (device.maxOutputChannels === 0)
|
|
117
|
-
throw new Error(`device "${device.id}" does not have any output channels (required by read/write mode)`)
|
|
118
|
-
this.log("info", `resolved "${this.params.device}" to duplex device "${device.id}"`)
|
|
119
|
-
this.io = PortAudio.AudioIO({
|
|
120
|
-
inOptions: {
|
|
121
|
-
deviceId: device.id,
|
|
122
|
-
channelCount: this.config.audioChannels,
|
|
123
|
-
sampleRate: this.config.audioSampleRate,
|
|
124
|
-
sampleFormat: this.config.audioBitDepth,
|
|
125
|
-
highwaterMark
|
|
126
|
-
},
|
|
127
|
-
outOptions: {
|
|
128
|
-
deviceId: device.id,
|
|
129
|
-
channelCount: this.config.audioChannels,
|
|
130
|
-
sampleRate: this.config.audioSampleRate,
|
|
131
|
-
sampleFormat: this.config.audioBitDepth,
|
|
132
|
-
highwaterMark
|
|
133
|
-
}
|
|
134
|
-
})
|
|
135
|
-
this.stream = this.io as unknown as Stream.Duplex
|
|
136
|
-
|
|
137
|
-
/* convert regular stream into object-mode stream */
|
|
138
|
-
const wrapper1 = util.createTransformStreamForWritableSide()
|
|
139
|
-
const wrapper2 = util.createTransformStreamForReadableSide("audio", () => this.timeZero)
|
|
140
|
-
this.stream = Stream.compose(wrapper1, this.stream, wrapper2)
|
|
141
|
-
}
|
|
142
|
-
else if (this.params.mode === "r") {
|
|
143
|
-
/* input device */
|
|
144
|
-
if (device.maxInputChannels === 0)
|
|
145
|
-
throw new Error(`device "${device.id}" does not have any input channels (required by read mode)`)
|
|
146
|
-
this.log("info", `resolved "${this.params.device}" to input device "${device.id}"`)
|
|
147
|
-
this.io = PortAudio.AudioIO({
|
|
148
|
-
inOptions: {
|
|
149
|
-
deviceId: device.id,
|
|
150
|
-
channelCount: this.config.audioChannels,
|
|
151
|
-
sampleRate: this.config.audioSampleRate,
|
|
152
|
-
sampleFormat: this.config.audioBitDepth,
|
|
153
|
-
highwaterMark
|
|
154
|
-
}
|
|
155
|
-
})
|
|
156
|
-
this.stream = this.io as unknown as Stream.Readable
|
|
157
|
-
|
|
158
|
-
/* convert regular stream into object-mode stream */
|
|
159
|
-
const wrapper = util.createTransformStreamForReadableSide("audio", () => this.timeZero)
|
|
160
|
-
this.stream = Stream.compose(this.stream, wrapper)
|
|
161
|
-
}
|
|
162
|
-
else if (this.params.mode === "w") {
|
|
163
|
-
/* output device */
|
|
164
|
-
if (device.maxOutputChannels === 0)
|
|
165
|
-
throw new Error(`device "${device.id}" does not have any output channels (required by write mode)`)
|
|
166
|
-
this.log("info", `resolved "${this.params.device}" to output device "${device.id}"`)
|
|
167
|
-
this.io = PortAudio.AudioIO({
|
|
168
|
-
outOptions: {
|
|
169
|
-
deviceId: device.id,
|
|
170
|
-
channelCount: this.config.audioChannels,
|
|
171
|
-
sampleRate: this.config.audioSampleRate,
|
|
172
|
-
sampleFormat: this.config.audioBitDepth,
|
|
173
|
-
highwaterMark
|
|
174
|
-
}
|
|
175
|
-
})
|
|
176
|
-
this.stream = this.io as unknown as Stream.Writable
|
|
177
|
-
|
|
178
|
-
/* convert regular stream into object-mode stream */
|
|
179
|
-
const wrapper = util.createTransformStreamForWritableSide()
|
|
180
|
-
this.stream = Stream.compose(wrapper, this.stream)
|
|
181
|
-
}
|
|
182
|
-
else
|
|
183
|
-
throw new Error(`device "${device.id}" does not have any input or output channels`)
|
|
185
|
+
/* establish device stream */
|
|
186
|
+
if (this.params.mode === "rw")
|
|
187
|
+
this.setupDuplexStream(device, highwaterMark)
|
|
188
|
+
else if (this.params.mode === "r")
|
|
189
|
+
this.setupInputStream(device, highwaterMark)
|
|
190
|
+
else if (this.params.mode === "w")
|
|
191
|
+
this.setupOutputStream(device, highwaterMark)
|
|
184
192
|
|
|
185
193
|
/* pass-through PortAudio errors */
|
|
186
|
-
this.io
|
|
194
|
+
this.io!.on("error", (err) => {
|
|
187
195
|
this.emit("error", err)
|
|
188
196
|
})
|
|
189
197
|
|
|
190
198
|
/* start PortAudio */
|
|
191
|
-
this.io
|
|
199
|
+
this.io!.start()
|
|
192
200
|
}
|
|
193
201
|
|
|
194
202
|
/* close node */
|
|
195
203
|
async close () {
|
|
196
204
|
/* shutdown PortAudio */
|
|
197
205
|
if (this.io !== null) {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
await
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
206
|
+
const catchHandler = (err: unknown) => {
|
|
207
|
+
const error = util.ensureError(err)
|
|
208
|
+
if (!error.message.match(/AudioIO Quit expects 1 argument/))
|
|
209
|
+
throw error
|
|
210
|
+
}
|
|
211
|
+
await Promise.race([
|
|
212
|
+
util.timeoutPromise(2 * 1000, "PortAudio abort timeout"),
|
|
213
|
+
new Promise<void>((resolve) => {
|
|
214
|
+
this.io!.abort(() => {
|
|
215
|
+
resolve()
|
|
216
|
+
})
|
|
217
|
+
}).catch(catchHandler)
|
|
218
|
+
])
|
|
219
|
+
await Promise.race([
|
|
220
|
+
util.timeoutPromise(2 * 1000, "PortAudio quit timeout"),
|
|
221
|
+
new Promise<void>((resolve) => {
|
|
222
|
+
this.io!.quit(() => {
|
|
223
|
+
resolve()
|
|
224
|
+
})
|
|
225
|
+
}).catch(catchHandler)
|
|
226
|
+
])
|
|
208
227
|
this.io = null
|
|
209
228
|
}
|
|
210
229
|
}
|
|
@@ -4,6 +4,13 @@
|
|
|
4
4
|
** Licensed under GPL 3.0 <https://spdx.org/licenses/GPL-3.0-only>
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
/* helper function for promise-based timeout */
|
|
8
|
+
export function timeoutPromise (duration: number = 10 * 1000, info = "timeout") {
|
|
9
|
+
return new Promise<void>((resolve, reject) => {
|
|
10
|
+
setTimeout(() => { reject(new Error(info)) }, duration)
|
|
11
|
+
})
|
|
12
|
+
}
|
|
13
|
+
|
|
7
14
|
/* helper function for retrieving an Error object */
|
|
8
15
|
export function ensureError (error: unknown, prefix?: string, debug = false): Error {
|
|
9
16
|
if (error instanceof Error && prefix === undefined && debug === false)
|
|
@@ -612,4 +612,4 @@ and limitations under the License.
|
|
|
612
612
|
`)}getSetCookie(){return this.get("set-cookie")||[]}get[Symbol.toStringTag](){return"AxiosHeaders"}static from(l){return l instanceof this?l:new this(l)}static concat(l,...e){const s=new this(l);return e.forEach(t=>s.set(t)),s}static accessor(l){const s=(this[$7]=this[$7]={accessors:{}}).accessors,t=this.prototype;function r(n){const o=U3(n);s[o]||(Zo(t,n),s[o]=!0)}return E.isArray(l)?l.forEach(r):r(l),this}};h2.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-Agent","Authorization"]);E.reduceDescriptors(h2.prototype,({value:c},l)=>{let e=l[0].toUpperCase()+l.slice(1);return{get:()=>c,set(s){this[e]=s}}});E.freezeMethods(h2);function a5(c,l){const e=this||M0,s=l||e,t=h2.from(s.headers);let r=s.data;return E.forEach(c,function(o){r=o.call(e,r,t.normalize(),l?l.status:void 0)}),t.normalize(),r}function yl(c){return!!(c&&c.__CANCEL__)}function E3(c,l,e){L1.call(this,c??"canceled",L1.ERR_CANCELED,l,e),this.name="CanceledError"}E.inherits(E3,L1,{__CANCEL__:!0});function wl(c,l,e){const s=e.config.validateStatus;!e.status||!s||s(e.status)?c(e):l(new L1("Request failed with status code "+e.status,[L1.ERR_BAD_REQUEST,L1.ERR_BAD_RESPONSE][Math.floor(e.status/100)-4],e.config,e.request,e))}function Xo(c){const l=/^([-+\w]{1,25})(:?\/\/|:)/.exec(c);return l&&l[1]||""}function Qo(c,l){c=c||10;const e=new Array(c),s=new Array(c);let t=0,r=0,n;return l=l!==void 0?l:1e3,function(u){const M=Date.now(),z=s[r];n||(n=M),e[t]=u,s[t]=M;let C=r,O=0;for(;C!==t;)O+=e[C++],C=C%c;if(t=(t+1)%c,t===r&&(r=(r+1)%c),M-n<l)return;const T=z&&M-z;return T?Math.round(O*1e3/T):void 0}}function cf(c,l){let e=0,s=1e3/l,t,r;const n=(M,z=Date.now())=>{e=z,t=null,r&&(clearTimeout(r),r=null),c(...M)};return[(...M)=>{const z=Date.now(),C=z-e;C>=s?n(M,z):(t=M,r||(r=setTimeout(()=>{r=null,n(t)},s-C)))},()=>t&&n(t)]}const K0=(c,l,e=3)=>{let s=0;const t=Qo(50,250);return cf(r=>{const n=r.loaded,o=r.lengthComputable?r.total:void 0,u=n-s,M=t(u),z=n<=o;s=n;const C={loaded:n,total:o,progress:o?n/o:void 0,bytes:u,rate:M||void 0,estimated:M&&o&&z?(o-n)/M:void 0,event:r,lengthComputable:o!=null,[l?"download":"upload"]:!0};c(C)},e)},V7=(c,l)=>{const e=c!=null;return[s=>l[0]({lengthComputable:e,total:c,loaded:s}),l[1]]},G7=c=>(...l)=>E.asap(()=>c(...l)),lf=s2.hasStandardBrowserEnv?((c,l)=>e=>(e=new URL(e,s2.origin),c.protocol===e.protocol&&c.host===e.host&&(l||c.port===e.port)))(new URL(s2.origin),s2.navigator&&/(msie|trident)/i.test(s2.navigator.userAgent)):()=>!0,ef=s2.hasStandardBrowserEnv?{write(c,l,e,s,t,r){const n=[c+"="+encodeURIComponent(l)];E.isNumber(e)&&n.push("expires="+new Date(e).toGMTString()),E.isString(s)&&n.push("path="+s),E.isString(t)&&n.push("domain="+t),r===!0&&n.push("secure"),document.cookie=n.join("; ")},read(c){const l=document.cookie.match(new RegExp("(^|;\\s*)("+c+")=([^;]*)"));return l?decodeURIComponent(l[3]):null},remove(c){this.write(c,"",Date.now()-864e5)}}:{write(){},read(){return null},remove(){}};function sf(c){return/^([a-z][a-z\d+\-.]*:)?\/\//i.test(c)}function tf(c,l){return l?c.replace(/\/?\/$/,"")+"/"+l.replace(/^\/+/,""):c}function Sl(c,l,e){let s=!sf(l);return c&&(s||e==!1)?tf(c,l):l}const J7=c=>c instanceof h2?{...c}:c;function l3(c,l){l=l||{};const e={};function s(M,z,C,O){return E.isPlainObject(M)&&E.isPlainObject(z)?E.merge.call({caseless:O},M,z):E.isPlainObject(z)?E.merge({},z):E.isArray(z)?z.slice():z}function t(M,z,C,O){if(E.isUndefined(z)){if(!E.isUndefined(M))return s(void 0,M,C,O)}else return s(M,z,C,O)}function r(M,z){if(!E.isUndefined(z))return s(void 0,z)}function n(M,z){if(E.isUndefined(z)){if(!E.isUndefined(M))return s(void 0,M)}else return s(void 0,z)}function o(M,z,C){if(C in l)return s(M,z);if(C in c)return s(void 0,M)}const u={url:r,method:r,data:r,baseURL:n,transformRequest:n,transformResponse:n,paramsSerializer:n,timeout:n,timeoutMessage:n,withCredentials:n,withXSRFToken:n,adapter:n,responseType:n,xsrfCookieName:n,xsrfHeaderName:n,onUploadProgress:n,onDownloadProgress:n,decompress:n,maxContentLength:n,maxBodyLength:n,beforeRedirect:n,transport:n,httpAgent:n,httpsAgent:n,cancelToken:n,socketPath:n,responseEncoding:n,validateStatus:o,headers:(M,z,C)=>t(J7(M),J7(z),C,!0)};return E.forEach(Object.keys({...c,...l}),function(z){const C=u[z]||t,O=C(c[z],l[z],z);E.isUndefined(O)&&C!==o||(e[z]=O)}),e}const vl=c=>{const l=l3({},c);let{data:e,withXSRFToken:s,xsrfHeaderName:t,xsrfCookieName:r,headers:n,auth:o}=l;l.headers=n=h2.from(n),l.url=gl(Sl(l.baseURL,l.url,l.allowAbsoluteUrls),c.params,c.paramsSerializer),o&&n.set("Authorization","Basic "+btoa((o.username||"")+":"+(o.password?unescape(encodeURIComponent(o.password)):"")));let u;if(E.isFormData(e)){if(s2.hasStandardBrowserEnv||s2.hasStandardBrowserWebWorkerEnv)n.setContentType(void 0);else if((u=n.getContentType())!==!1){const[M,...z]=u?u.split(";").map(C=>C.trim()).filter(Boolean):[];n.setContentType([M||"multipart/form-data",...z].join("; "))}}if(s2.hasStandardBrowserEnv&&(s&&E.isFunction(s)&&(s=s(l)),s||s!==!1&&lf(l.url))){const M=t&&r&&ef.read(r);M&&n.set(t,M)}return l},rf=typeof XMLHttpRequest<"u",nf=rf&&function(c){return new Promise(function(e,s){const t=vl(c);let r=t.data;const n=h2.from(t.headers).normalize();let{responseType:o,onUploadProgress:u,onDownloadProgress:M}=t,z,C,O,T,F;function B(){T&&T(),F&&F(),t.cancelToken&&t.cancelToken.unsubscribe(z),t.signal&&t.signal.removeEventListener("abort",z)}let J=new XMLHttpRequest;J.open(t.method.toUpperCase(),t.url,!0),J.timeout=t.timeout;function Y(){if(!J)return;const s1=h2.from("getAllResponseHeaders"in J&&J.getAllResponseHeaders()),f1={data:!o||o==="text"||o==="json"?J.responseText:J.response,status:J.status,statusText:J.statusText,headers:s1,config:c,request:J};wl(function(k1){e(k1),B()},function(k1){s(k1),B()},f1),J=null}"onloadend"in J?J.onloadend=Y:J.onreadystatechange=function(){!J||J.readyState!==4||J.status===0&&!(J.responseURL&&J.responseURL.indexOf("file:")===0)||setTimeout(Y)},J.onabort=function(){J&&(s(new L1("Request aborted",L1.ECONNABORTED,c,J)),J=null)},J.onerror=function(){s(new L1("Network Error",L1.ERR_NETWORK,c,J)),J=null},J.ontimeout=function(){let $=t.timeout?"timeout of "+t.timeout+"ms exceeded":"timeout exceeded";const f1=t.transitional||bl;t.timeoutErrorMessage&&($=t.timeoutErrorMessage),s(new L1($,f1.clarifyTimeoutError?L1.ETIMEDOUT:L1.ECONNABORTED,c,J)),J=null},r===void 0&&n.setContentType(null),"setRequestHeader"in J&&E.forEach(n.toJSON(),function($,f1){J.setRequestHeader(f1,$)}),E.isUndefined(t.withCredentials)||(J.withCredentials=!!t.withCredentials),o&&o!=="json"&&(J.responseType=t.responseType),M&&([O,F]=K0(M,!0),J.addEventListener("progress",O)),u&&J.upload&&([C,T]=K0(u),J.upload.addEventListener("progress",C),J.upload.addEventListener("loadend",T)),(t.cancelToken||t.signal)&&(z=s1=>{J&&(s(!s1||s1.type?new E3(null,c,J):s1),J.abort(),J=null)},t.cancelToken&&t.cancelToken.subscribe(z),t.signal&&(t.signal.aborted?z():t.signal.addEventListener("abort",z)));const a1=Xo(t.url);if(a1&&s2.protocols.indexOf(a1)===-1){s(new L1("Unsupported protocol "+a1+":",L1.ERR_BAD_REQUEST,c));return}J.send(r||null)})},af=(c,l)=>{const{length:e}=c=c?c.filter(Boolean):[];if(l||e){let s=new AbortController,t;const r=function(M){if(!t){t=!0,o();const z=M instanceof Error?M:this.reason;s.abort(z instanceof L1?z:new E3(z instanceof Error?z.message:z))}};let n=l&&setTimeout(()=>{n=null,r(new L1(`timeout ${l} of ms exceeded`,L1.ETIMEDOUT))},l);const o=()=>{c&&(n&&clearTimeout(n),n=null,c.forEach(M=>{M.unsubscribe?M.unsubscribe(r):M.removeEventListener("abort",r)}),c=null)};c.forEach(M=>M.addEventListener("abort",r));const{signal:u}=s;return u.unsubscribe=()=>E.asap(o),u}},of=function*(c,l){let e=c.byteLength;if(e<l){yield c;return}let s=0,t;for(;s<e;)t=s+l,yield c.slice(s,t),s=t},ff=async function*(c,l){for await(const e of zf(c))yield*of(e,l)},zf=async function*(c){if(c[Symbol.asyncIterator]){yield*c;return}const l=c.getReader();try{for(;;){const{done:e,value:s}=await l.read();if(e)break;yield s}}finally{await l.cancel()}},K7=(c,l,e,s)=>{const t=ff(c,l);let r=0,n,o=u=>{n||(n=!0,s&&s(u))};return new ReadableStream({async pull(u){try{const{done:M,value:z}=await t.next();if(M){o(),u.close();return}let C=z.byteLength;if(e){let O=r+=C;e(O)}u.enqueue(new Uint8Array(z))}catch(M){throw o(M),M}},cancel(u){return o(u),t.return()}},{highWaterMark:2})},v6=typeof fetch=="function"&&typeof Request=="function"&&typeof Response=="function",_l=v6&&typeof ReadableStream=="function",uf=v6&&(typeof TextEncoder=="function"?(c=>l=>c.encode(l))(new TextEncoder):async c=>new Uint8Array(await new Response(c).arrayBuffer())),kl=(c,...l)=>{try{return!!c(...l)}catch{return!1}},df=_l&&kl(()=>{let c=!1;const l=new Request(s2.origin,{body:new ReadableStream,method:"POST",get duplex(){return c=!0,"half"}}).headers.has("Content-Type");return c&&!l}),Z7=64*1024,A5=_l&&kl(()=>E.isReadableStream(new Response("").body)),Z0={stream:A5&&(c=>c.body)};v6&&(c=>{["text","arrayBuffer","blob","formData","stream"].forEach(l=>{!Z0[l]&&(Z0[l]=E.isFunction(c[l])?e=>e[l]():(e,s)=>{throw new L1(`Response type '${l}' is not supported`,L1.ERR_NOT_SUPPORT,s)})})})(new Response);const mf=async c=>{if(c==null)return 0;if(E.isBlob(c))return c.size;if(E.isSpecCompliantForm(c))return(await new Request(s2.origin,{method:"POST",body:c}).arrayBuffer()).byteLength;if(E.isArrayBufferView(c)||E.isArrayBuffer(c))return c.byteLength;if(E.isURLSearchParams(c)&&(c=c+""),E.isString(c))return(await uf(c)).byteLength},Mf=async(c,l)=>{const e=E.toFiniteNumber(c.getContentLength());return e??mf(l)},hf=v6&&(async c=>{let{url:l,method:e,data:s,signal:t,cancelToken:r,timeout:n,onDownloadProgress:o,onUploadProgress:u,responseType:M,headers:z,withCredentials:C="same-origin",fetchOptions:O}=vl(c);M=M?(M+"").toLowerCase():"text";let T=af([t,r&&r.toAbortSignal()],n),F;const B=T&&T.unsubscribe&&(()=>{T.unsubscribe()});let J;try{if(u&&df&&e!=="get"&&e!=="head"&&(J=await Mf(z,s))!==0){let f1=new Request(l,{method:"POST",body:s,duplex:"half"}),y1;if(E.isFormData(s)&&(y1=f1.headers.get("content-type"))&&z.setContentType(y1),f1.body){const[k1,k]=V7(J,K0(G7(u)));s=K7(f1.body,Z7,k1,k)}}E.isString(C)||(C=C?"include":"omit");const Y="credentials"in Request.prototype;F=new Request(l,{...O,signal:T,method:e.toUpperCase(),headers:z.normalize().toJSON(),body:s,duplex:"half",credentials:Y?C:void 0});let a1=await fetch(F,O);const s1=A5&&(M==="stream"||M==="response");if(A5&&(o||s1&&B)){const f1={};["status","statusText","headers"].forEach(_=>{f1[_]=a1[_]});const y1=E.toFiniteNumber(a1.headers.get("content-length")),[k1,k]=o&&V7(y1,K0(G7(o),!0))||[];a1=new Response(K7(a1.body,Z7,k1,()=>{k&&k(),B&&B()}),f1)}M=M||"text";let $=await Z0[E.findKey(Z0,M)||"text"](a1,c);return!s1&&B&&B(),await new Promise((f1,y1)=>{wl(f1,y1,{data:$,headers:h2.from(a1.headers),status:a1.status,statusText:a1.statusText,config:c,request:F})})}catch(Y){throw B&&B(),Y&&Y.name==="TypeError"&&/Load failed|fetch/i.test(Y.message)?Object.assign(new L1("Network Error",L1.ERR_NETWORK,c,F),{cause:Y.cause||Y}):L1.from(Y,Y&&Y.code,c,F)}}),P5={http:Oo,xhr:nf,fetch:hf};E.forEach(P5,(c,l)=>{if(c){try{Object.defineProperty(c,"name",{value:l})}catch{}Object.defineProperty(c,"adapterName",{value:l})}});const X7=c=>`- ${c}`,Lf=c=>E.isFunction(c)||c===null||c===!1,xl={getAdapter:c=>{c=E.isArray(c)?c:[c];const{length:l}=c;let e,s;const t={};for(let r=0;r<l;r++){e=c[r];let n;if(s=e,!Lf(e)&&(s=P5[(n=String(e)).toLowerCase()],s===void 0))throw new L1(`Unknown adapter '${n}'`);if(s)break;t[n||"#"+r]=s}if(!s){const r=Object.entries(t).map(([o,u])=>`adapter ${o} `+(u===!1?"is not supported by the environment":"is not available in the build"));let n=l?r.length>1?`since :
|
|
613
613
|
`+r.map(X7).join(`
|
|
614
614
|
`):" "+X7(r[0]):"as no adapter specified";throw new L1("There is no suitable adapter to dispatch the request "+n,"ERR_NOT_SUPPORT")}return s},adapters:P5};function i5(c){if(c.cancelToken&&c.cancelToken.throwIfRequested(),c.signal&&c.signal.aborted)throw new E3(null,c)}function Q7(c){return i5(c),c.headers=h2.from(c.headers),c.data=a5.call(c,c.transformRequest),["post","put","patch"].indexOf(c.method)!==-1&&c.headers.setContentType("application/x-www-form-urlencoded",!1),xl.getAdapter(c.adapter||M0.adapter)(c).then(function(s){return i5(c),s.data=a5.call(c,c.transformResponse,s),s.headers=h2.from(s.headers),s},function(s){return yl(s)||(i5(c),s&&s.response&&(s.response.data=a5.call(c,c.transformResponse,s.response),s.response.headers=h2.from(s.response.headers))),Promise.reject(s)})}const El="1.11.0",_6={};["object","boolean","number","function","string","symbol"].forEach((c,l)=>{_6[c]=function(s){return typeof s===c||"a"+(l<1?"n ":" ")+c}});const c9={};_6.transitional=function(l,e,s){function t(r,n){return"[Axios v"+El+"] Transitional option '"+r+"'"+n+(s?". "+s:"")}return(r,n,o)=>{if(l===!1)throw new L1(t(n," has been removed"+(e?" in "+e:"")),L1.ERR_DEPRECATED);return e&&!c9[n]&&(c9[n]=!0,console.warn(t(n," has been deprecated since v"+e+" and will be removed in the near future"))),l?l(r,n,o):!0}};_6.spelling=function(l){return(e,s)=>(console.warn(`${s} is likely a misspelling of ${l}`),!0)};function pf(c,l,e){if(typeof c!="object")throw new L1("options must be an object",L1.ERR_BAD_OPTION_VALUE);const s=Object.keys(c);let t=s.length;for(;t-- >0;){const r=s[t],n=l[r];if(n){const o=c[r],u=o===void 0||n(o,r,c);if(u!==!0)throw new L1("option "+r+" must be "+u,L1.ERR_BAD_OPTION_VALUE);continue}if(e!==!0)throw new L1("Unknown option "+r,L1.ERR_BAD_OPTION)}}const F0={assertOptions:pf,validators:_6},Y2=F0.validators;let Q4=class{constructor(l){this.defaults=l||{},this.interceptors={request:new H7,response:new H7}}async request(l,e){try{return await this._request(l,e)}catch(s){if(s instanceof Error){let t={};Error.captureStackTrace?Error.captureStackTrace(t):t=new Error;const r=t.stack?t.stack.replace(/^.+\n/,""):"";try{s.stack?r&&!String(s.stack).endsWith(r.replace(/^.+\n.+\n/,""))&&(s.stack+=`
|
|
615
|
-
`+r):s.stack=r}catch{}}throw s}}_request(l,e){typeof l=="string"?(e=e||{},e.url=l):e=l||{},e=l3(this.defaults,e);const{transitional:s,paramsSerializer:t,headers:r}=e;s!==void 0&&F0.assertOptions(s,{silentJSONParsing:Y2.transitional(Y2.boolean),forcedJSONParsing:Y2.transitional(Y2.boolean),clarifyTimeoutError:Y2.transitional(Y2.boolean)},!1),t!=null&&(E.isFunction(t)?e.paramsSerializer={serialize:t}:F0.assertOptions(t,{encode:Y2.function,serialize:Y2.function},!0)),e.allowAbsoluteUrls!==void 0||(this.defaults.allowAbsoluteUrls!==void 0?e.allowAbsoluteUrls=this.defaults.allowAbsoluteUrls:e.allowAbsoluteUrls=!0),F0.assertOptions(e,{baseUrl:Y2.spelling("baseURL"),withXsrfToken:Y2.spelling("withXSRFToken")},!0),e.method=(e.method||this.defaults.method||"get").toLowerCase();let n=r&&E.merge(r.common,r[e.method]);r&&E.forEach(["delete","get","head","post","put","patch","common"],F=>{delete r[F]}),e.headers=h2.concat(n,r);const o=[];let u=!0;this.interceptors.request.forEach(function(B){typeof B.runWhen=="function"&&B.runWhen(e)===!1||(u=u&&B.synchronous,o.unshift(B.fulfilled,B.rejected))});const M=[];this.interceptors.response.forEach(function(B){M.push(B.fulfilled,B.rejected)});let z,C=0,O;if(!u){const F=[Q7.bind(this),void 0];for(F.unshift(...o),F.push(...M),O=F.length,z=Promise.resolve(e);C<O;)z=z.then(F[C++],F[C++]);return z}O=o.length;let T=e;for(C=0;C<O;){const F=o[C++],B=o[C++];try{T=F(T)}catch(J){B.call(this,J);break}}try{z=Q7.call(this,T)}catch(F){return Promise.reject(F)}for(C=0,O=M.length;C<O;)z=z.then(M[C++],M[C++]);return z}getUri(l){l=l3(this.defaults,l);const e=Sl(l.baseURL,l.url,l.allowAbsoluteUrls);return gl(e,l.params,l.paramsSerializer)}};E.forEach(["delete","get","head","options"],function(l){Q4.prototype[l]=function(e,s){return this.request(l3(s||{},{method:l,url:e,data:(s||{}).data}))}});E.forEach(["post","put","patch"],function(l){function e(s){return function(r,n,o){return this.request(l3(o||{},{method:l,headers:s?{"Content-Type":"multipart/form-data"}:{},url:r,data:n}))}}Q4.prototype[l]=e(),Q4.prototype[l+"Form"]=e(!0)});let gf=class Ol{constructor(l){if(typeof l!="function")throw new TypeError("executor must be a function.");let e;this.promise=new Promise(function(r){e=r});const s=this;this.promise.then(t=>{if(!s._listeners)return;let r=s._listeners.length;for(;r-- >0;)s._listeners[r](t);s._listeners=null}),this.promise.then=t=>{let r;const n=new Promise(o=>{s.subscribe(o),r=o}).then(t);return n.cancel=function(){s.unsubscribe(r)},n},l(function(r,n,o){s.reason||(s.reason=new E3(r,n,o),e(s.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(l){if(this.reason){l(this.reason);return}this._listeners?this._listeners.push(l):this._listeners=[l]}unsubscribe(l){if(!this._listeners)return;const e=this._listeners.indexOf(l);e!==-1&&this._listeners.splice(e,1)}toAbortSignal(){const l=new AbortController,e=s=>{l.abort(s)};return this.subscribe(e),l.signal.unsubscribe=()=>this.unsubscribe(e),l.signal}static source(){let l;return{token:new Ol(function(t){l=t}),cancel:l}}};function bf(c){return function(e){return c.apply(null,e)}}function Cf(c){return E.isObject(c)&&c.isAxiosError===!0}const j5={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511};Object.entries(j5).forEach(([c,l])=>{j5[l]=c});function Tl(c){const l=new Q4(c),e=rl(Q4.prototype.request,l);return E.extend(e,Q4.prototype,l,{allOwnKeys:!0}),E.extend(e,l,null,{allOwnKeys:!0}),e.create=function(t){return Tl(l3(c,t))},e}const J1=Tl(M0);J1.Axios=Q4;J1.CanceledError=E3;J1.CancelToken=gf;J1.isCancel=yl;J1.VERSION=El;J1.toFormData=S6;J1.AxiosError=L1;J1.Cancel=J1.CanceledError;J1.all=function(l){return Promise.all(l)};J1.spread=bf;J1.isAxiosError=Cf;J1.mergeConfig=l3;J1.AxiosHeaders=h2;J1.formToJSON=c=>Cl(E.isHTMLForm(c)?new FormData(c):c);J1.getAdapter=xl.getAdapter;J1.HttpStatusCode=j5;J1.default=J1;const{Axios:jf,AxiosError:Ff,CanceledError:Rf,isCancel:Nf,CancelToken:If,VERSION:Uf,all:Bf,Cancel:Yf,isAxiosError:qf,spread:Wf,toFormData:Hf,AxiosHeaders:$f,HttpStatusCode:Vf,formToJSON:Gf,getAdapter:Jf,mergeConfig:Kf}=J1,yf={class:"app"},wf={class:"dashboard"},Sf={class:"block-content"},vf={key:0,class:"audio-col"},_f={class:"audio-value"},kf={class:"block-name"},xf=A9({name:"app",
|
|
615
|
+
`+r):s.stack=r}catch{}}throw s}}_request(l,e){typeof l=="string"?(e=e||{},e.url=l):e=l||{},e=l3(this.defaults,e);const{transitional:s,paramsSerializer:t,headers:r}=e;s!==void 0&&F0.assertOptions(s,{silentJSONParsing:Y2.transitional(Y2.boolean),forcedJSONParsing:Y2.transitional(Y2.boolean),clarifyTimeoutError:Y2.transitional(Y2.boolean)},!1),t!=null&&(E.isFunction(t)?e.paramsSerializer={serialize:t}:F0.assertOptions(t,{encode:Y2.function,serialize:Y2.function},!0)),e.allowAbsoluteUrls!==void 0||(this.defaults.allowAbsoluteUrls!==void 0?e.allowAbsoluteUrls=this.defaults.allowAbsoluteUrls:e.allowAbsoluteUrls=!0),F0.assertOptions(e,{baseUrl:Y2.spelling("baseURL"),withXsrfToken:Y2.spelling("withXSRFToken")},!0),e.method=(e.method||this.defaults.method||"get").toLowerCase();let n=r&&E.merge(r.common,r[e.method]);r&&E.forEach(["delete","get","head","post","put","patch","common"],F=>{delete r[F]}),e.headers=h2.concat(n,r);const o=[];let u=!0;this.interceptors.request.forEach(function(B){typeof B.runWhen=="function"&&B.runWhen(e)===!1||(u=u&&B.synchronous,o.unshift(B.fulfilled,B.rejected))});const M=[];this.interceptors.response.forEach(function(B){M.push(B.fulfilled,B.rejected)});let z,C=0,O;if(!u){const F=[Q7.bind(this),void 0];for(F.unshift(...o),F.push(...M),O=F.length,z=Promise.resolve(e);C<O;)z=z.then(F[C++],F[C++]);return z}O=o.length;let T=e;for(C=0;C<O;){const F=o[C++],B=o[C++];try{T=F(T)}catch(J){B.call(this,J);break}}try{z=Q7.call(this,T)}catch(F){return Promise.reject(F)}for(C=0,O=M.length;C<O;)z=z.then(M[C++],M[C++]);return z}getUri(l){l=l3(this.defaults,l);const e=Sl(l.baseURL,l.url,l.allowAbsoluteUrls);return gl(e,l.params,l.paramsSerializer)}};E.forEach(["delete","get","head","options"],function(l){Q4.prototype[l]=function(e,s){return this.request(l3(s||{},{method:l,url:e,data:(s||{}).data}))}});E.forEach(["post","put","patch"],function(l){function e(s){return function(r,n,o){return this.request(l3(o||{},{method:l,headers:s?{"Content-Type":"multipart/form-data"}:{},url:r,data:n}))}}Q4.prototype[l]=e(),Q4.prototype[l+"Form"]=e(!0)});let gf=class Ol{constructor(l){if(typeof l!="function")throw new TypeError("executor must be a function.");let e;this.promise=new Promise(function(r){e=r});const s=this;this.promise.then(t=>{if(!s._listeners)return;let r=s._listeners.length;for(;r-- >0;)s._listeners[r](t);s._listeners=null}),this.promise.then=t=>{let r;const n=new Promise(o=>{s.subscribe(o),r=o}).then(t);return n.cancel=function(){s.unsubscribe(r)},n},l(function(r,n,o){s.reason||(s.reason=new E3(r,n,o),e(s.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(l){if(this.reason){l(this.reason);return}this._listeners?this._listeners.push(l):this._listeners=[l]}unsubscribe(l){if(!this._listeners)return;const e=this._listeners.indexOf(l);e!==-1&&this._listeners.splice(e,1)}toAbortSignal(){const l=new AbortController,e=s=>{l.abort(s)};return this.subscribe(e),l.signal.unsubscribe=()=>this.unsubscribe(e),l.signal}static source(){let l;return{token:new Ol(function(t){l=t}),cancel:l}}};function bf(c){return function(e){return c.apply(null,e)}}function Cf(c){return E.isObject(c)&&c.isAxiosError===!0}const j5={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511};Object.entries(j5).forEach(([c,l])=>{j5[l]=c});function Tl(c){const l=new Q4(c),e=rl(Q4.prototype.request,l);return E.extend(e,Q4.prototype,l,{allOwnKeys:!0}),E.extend(e,l,null,{allOwnKeys:!0}),e.create=function(t){return Tl(l3(c,t))},e}const J1=Tl(M0);J1.Axios=Q4;J1.CanceledError=E3;J1.CancelToken=gf;J1.isCancel=yl;J1.VERSION=El;J1.toFormData=S6;J1.AxiosError=L1;J1.Cancel=J1.CanceledError;J1.all=function(l){return Promise.all(l)};J1.spread=bf;J1.isAxiosError=Cf;J1.mergeConfig=l3;J1.AxiosHeaders=h2;J1.formToJSON=c=>Cl(E.isHTMLForm(c)?new FormData(c):c);J1.getAdapter=xl.getAdapter;J1.HttpStatusCode=j5;J1.default=J1;const{Axios:jf,AxiosError:Ff,CanceledError:Rf,isCancel:Nf,CancelToken:If,VERSION:Uf,all:Bf,Cancel:Yf,isAxiosError:qf,spread:Wf,toFormData:Hf,AxiosHeaders:$f,HttpStatusCode:Vf,formToJSON:Gf,getAdapter:Jf,mergeConfig:Kf}=J1,yf={class:"app"},wf={class:"dashboard"},Sf={class:"block-content"},vf={key:0,class:"audio-col"},_f={class:"audio-value"},kf={class:"block-name"},xf=A9({name:"app",data:()=>({info:[],ws:null}),async mounted(){const c=new URL("/api",document.location.href).toString(),l=await J1.get(`${c}/dashboard`);for(const e of l.data)e.type==="audio"?this.info.push({type:e.type,id:e.id,name:e.name,value:0,lastKind:""}):e.type==="text"&&this.info.push({type:e.type,id:e.id,name:e.name,value:[],lastKind:""});this.ws=new Oi(c,[],{reconnectionDelayGrowFactor:1.3,maxReconnectionDelay:4e3,minReconnectionDelay:1e3,connectionTimeout:4e3,minUptime:5e3}),this.ws.addEventListener("open",e=>{}),this.ws.addEventListener("message",e=>{let s;try{s=JSON.parse(e.data)}catch(u){this.log("ERROR","Failed to parse WebSocket message",{error:u,data:e.data});return}if(s.response!=="DASHBOARD")return;const[t,r,n,o]=s.args;for(const u of this.info)if(u.type===t&&u.id===r)if(u.type==="audio")n==="final"&&typeof o=="number"&&(u.value=o);else{if(typeof o=="string"){const M=u.value;u.lastKind==="intermediate"?M[M.length-1]=o:(M.push(o),u.value=M.slice(-20))}u.lastKind=n,this.$nextTick(()=>{for(const M of this.$refs.textCol)M.scrollTop=M.scrollHeight})}})},beforeUnmount(){this.ws&&(this.ws.close(),this.ws=null)},methods:{log(c,l,e=null){let t=`${l1().format("YYYY-MM-DD hh:mm:ss.SSS")} [${c}]: ${l}`;e!==null&&(t+=` (${Object.keys(e).map(r=>r+": "+JSON.stringify(e[r])).join(", ")})`),console.log(t)}}}),Ef=A9({...xf,setup(c){return(l,e)=>(k4(),q4("div",yf,[E4("div",wf,[(k4(!0),q4(T2,null,o7(l.info,s=>(k4(),q4("div",{key:s.id,class:"block"},[E4("div",Sf,[s.type==="audio"?(k4(),q4("div",vf,[E4("div",{class:"audio-meter",style:e6({height:100*(1-s.value/-60)+"%"})},[E4("div",_f,[tc(_0(s.value.toFixed(1))+" ",1),e[0]||(e[0]=E4("div",{class:"audio-unit"},"LUFS-S",-1))])],4)])):g7("",!0),s.type==="text"?(k4(),q4("div",{key:1,ref_for:!0,ref:"textCol",class:s6(["text-col",{intermediate:s.lastKind==="intermediate"}])},[(k4(!0),q4(T2,null,o7(s.value,t=>(k4(),q4("div",{key:t,class:"text-value"},_0(t),1))),128))],2)):g7("",!0)]),E4("div",kf,_0(s.name),1)]))),128))])]))}});document.addEventListener("DOMContentLoaded",c=>{(async()=>{bt(Ef).mount("#app")})().catch(l=>{console.error(`app: ERROR: top-level: ${l}`)})})});export default Of();
|
|
@@ -4,13 +4,11 @@
|
|
|
4
4
|
** Licensed under GPL 3.0 <https://spdx.org/licenses/GPL-3.0-only>
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import path from "node:path"
|
|
8
7
|
import * as Vite from "vite"
|
|
9
8
|
import VuePlugin from "@vitejs/plugin-vue"
|
|
10
9
|
import YAMLPlugin from "@rollup/plugin-yaml"
|
|
11
10
|
import { nodePolyfills } from "vite-plugin-node-polyfills"
|
|
12
11
|
import SvgLoader from "vite-svg-loader"
|
|
13
|
-
import { mkdirp } from "mkdirp"
|
|
14
12
|
|
|
15
13
|
export default Vite.defineConfig(({ command, mode }) => ({
|
|
16
14
|
logLevel: "info",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
<div v-bind:key="value"
|
|
29
29
|
v-for="value of block.value"
|
|
30
30
|
class="text-value">
|
|
31
|
-
{{ value
|
|
31
|
+
{{ value }}
|
|
32
32
|
</div>
|
|
33
33
|
</div>
|
|
34
34
|
</div>
|
|
@@ -149,22 +149,21 @@ type Info = {
|
|
|
149
149
|
value: number | string[],
|
|
150
150
|
lastKind: string
|
|
151
151
|
}
|
|
152
|
+
|
|
153
|
+
interface WebSocketEvent {
|
|
154
|
+
response: string,
|
|
155
|
+
args: [ string, string, string, number | string ]
|
|
156
|
+
}
|
|
157
|
+
|
|
152
158
|
export default defineComponent({
|
|
153
159
|
name: "app",
|
|
154
|
-
components: {
|
|
155
|
-
},
|
|
156
160
|
data: () => ({
|
|
157
161
|
info: [] as Info[],
|
|
158
|
-
ws:
|
|
162
|
+
ws: null as ReconnectingWebSocket | null
|
|
159
163
|
}),
|
|
160
|
-
created () {
|
|
161
|
-
},
|
|
162
164
|
async mounted () {
|
|
163
165
|
/* determine API URL */
|
|
164
|
-
|
|
165
|
-
url = url.replace(/#.+$/, "")
|
|
166
|
-
url = url.replace(/\/[^/]*$/, "")
|
|
167
|
-
url = url + "/api"
|
|
166
|
+
const url = new URL("/api", document.location.href).toString()
|
|
168
167
|
|
|
169
168
|
/* load dashboard configuration */
|
|
170
169
|
const response = await axios.get(`${url}/dashboard`)
|
|
@@ -186,7 +185,7 @@ export default defineComponent({
|
|
|
186
185
|
this.ws.addEventListener("open", (ev) => {
|
|
187
186
|
})
|
|
188
187
|
this.ws.addEventListener("message", (ev) => {
|
|
189
|
-
let event
|
|
188
|
+
let event: WebSocketEvent
|
|
190
189
|
try {
|
|
191
190
|
event = JSON.parse(ev.data)
|
|
192
191
|
}
|
|
@@ -196,22 +195,24 @@ export default defineComponent({
|
|
|
196
195
|
}
|
|
197
196
|
if (event.response !== "DASHBOARD")
|
|
198
197
|
return
|
|
198
|
+
|
|
199
|
+
/* extract dashboard update parameters: [ type, id, kind, value ] */
|
|
199
200
|
const [ type, id, kind, value ] = event.args
|
|
200
201
|
for (const block of this.info) {
|
|
201
202
|
if (block.type === type && block.id === id) {
|
|
202
203
|
if (block.type === "audio") {
|
|
203
|
-
if (kind === "final")
|
|
204
|
+
if (kind === "final" && typeof value === "number")
|
|
204
205
|
block.value = value
|
|
205
206
|
}
|
|
206
207
|
else {
|
|
207
|
-
if (
|
|
208
|
-
const arr = block.value as string[]
|
|
209
|
-
arr[arr.length - 1] = value
|
|
210
|
-
}
|
|
211
|
-
else {
|
|
208
|
+
if (typeof value === "string") {
|
|
212
209
|
const arr = block.value as string[]
|
|
213
|
-
|
|
214
|
-
|
|
210
|
+
if (block.lastKind === "intermediate")
|
|
211
|
+
arr[arr.length - 1] = value
|
|
212
|
+
else {
|
|
213
|
+
arr.push(value)
|
|
214
|
+
block.value = arr.slice(-20)
|
|
215
|
+
}
|
|
215
216
|
}
|
|
216
217
|
block.lastKind = kind
|
|
217
218
|
this.$nextTick(() => {
|
|
@@ -606,4 +606,4 @@ and limitations under the License.
|
|
|
606
606
|
* https://github.com/pladaria/reconnecting-websocket
|
|
607
607
|
* https://github.com/opensumi/reconnecting-websocket
|
|
608
608
|
* License MIT
|
|
609
|
-
*/var ai=function(){if(typeof WebSocket<"u")return WebSocket},ri=function(c){return typeof c<"u"&&!!c&&c.CLOSING===2},k4={maxReconnectionDelay:1e4,minReconnectionDelay:1e3+Math.random()*4e3,minUptime:5e3,reconnectionDelayGrowFactor:1.3,connectionTimeout:4e3,maxRetries:1/0,maxEnqueuedMessages:1/0},ni=(function(){function c(l,e,s){var t=this;s===void 0&&(s={}),this._listeners={error:[],message:[],open:[],close:[]},this._retryCount=-1,this._shouldReconnect=!0,this._connectLock=!1,this._binaryType="blob",this._closeCalled=!1,this._messageQueue=[],this.onclose=null,this.onerror=null,this.onmessage=null,this.onopen=null,this._handleOpen=function(a){t._debug("open event");var r=t._options.minUptime,i=r===void 0?k4.minUptime:r;clearTimeout(t._connectTimeout),t._uptimeTimeout=setTimeout(function(){return t._acceptOpen()},i),t._ws.binaryType=t._binaryType,t._messageQueue.forEach(function(f){var M;return(M=t._ws)===null||M===void 0?void 0:M.send(f)}),t._messageQueue=[],t.onopen&&t.onopen(a),t._listeners.open.forEach(function(f){return t._callEventListener(a,f)})},this._handleMessage=function(a){t._debug("message event"),t.onmessage&&t.onmessage(a),t._listeners.message.forEach(function(r){return t._callEventListener(a,r)})},this._handleError=function(a){t._debug("error event",a.message),t._disconnect(void 0,a.message==="TIMEOUT"?"timeout":void 0),t.onerror&&t.onerror(a),t._debug("exec error listeners"),t._listeners.error.forEach(function(r){return t._callEventListener(a,r)}),t._connect()},this._handleClose=function(a){t._debug("close event"),t._clearTimeouts(),t._shouldReconnect&&t._connect(),t.onclose&&t.onclose(a),t._listeners.close.forEach(function(r){return t._callEventListener(a,r)})},this._url=l,this._protocols=e,this._options=s,this._options.startClosed&&(this._shouldReconnect=!1),s.logger?this._logger=s.logger:this._logger=console,this._connect()}return Object.defineProperty(c,"CONNECTING",{get:function(){return 0},enumerable:!0,configurable:!0}),Object.defineProperty(c,"OPEN",{get:function(){return 1},enumerable:!0,configurable:!0}),Object.defineProperty(c,"CLOSING",{get:function(){return 2},enumerable:!0,configurable:!0}),Object.defineProperty(c,"CLOSED",{get:function(){return 3},enumerable:!0,configurable:!0}),Object.defineProperty(c.prototype,"CONNECTING",{get:function(){return c.CONNECTING},enumerable:!0,configurable:!0}),Object.defineProperty(c.prototype,"OPEN",{get:function(){return c.OPEN},enumerable:!0,configurable:!0}),Object.defineProperty(c.prototype,"CLOSING",{get:function(){return c.CLOSING},enumerable:!0,configurable:!0}),Object.defineProperty(c.prototype,"CLOSED",{get:function(){return c.CLOSED},enumerable:!0,configurable:!0}),Object.defineProperty(c.prototype,"binaryType",{get:function(){return this._ws?this._ws.binaryType:this._binaryType},set:function(l){this._binaryType=l,this._ws&&(this._ws.binaryType=l)},enumerable:!0,configurable:!0}),Object.defineProperty(c.prototype,"retryCount",{get:function(){return Math.max(this._retryCount,0)},enumerable:!0,configurable:!0}),Object.defineProperty(c.prototype,"bufferedAmount",{get:function(){var l=this._messageQueue.reduce(function(e,s){return typeof s=="string"?e+=s.length:s instanceof Blob?e+=s.size:e+=s.byteLength,e},0);return l+(this._ws?this._ws.bufferedAmount:0)},enumerable:!0,configurable:!0}),Object.defineProperty(c.prototype,"extensions",{get:function(){return this._ws?this._ws.extensions:""},enumerable:!0,configurable:!0}),Object.defineProperty(c.prototype,"protocol",{get:function(){return this._ws?this._ws.protocol:""},enumerable:!0,configurable:!0}),Object.defineProperty(c.prototype,"readyState",{get:function(){return this._ws?this._ws.readyState:this._options.startClosed?c.CLOSED:c.CONNECTING},enumerable:!0,configurable:!0}),Object.defineProperty(c.prototype,"url",{get:function(){return this._ws?this._ws.url:""},enumerable:!0,configurable:!0}),c.prototype.close=function(l,e){if(l===void 0&&(l=1e3),this._closeCalled=!0,this._shouldReconnect=!1,this._clearTimeouts(),!this._ws){this._debug("close enqueued: no ws instance");return}if(this._ws.readyState===this.CLOSED){this._debug("close: already closed");return}this._ws.close(l,e)},c.prototype.reconnect=function(l,e){this._shouldReconnect=!0,this._closeCalled=!1,this._retryCount=-1,!this._ws||this._ws.readyState===this.CLOSED?this._connect():(this._disconnect(l,e),this._connect())},c.prototype.send=function(l){if(this._ws&&this._ws.readyState===this.OPEN)this._debug("send",l),this._ws.send(l);else{var e=this._options.maxEnqueuedMessages,s=e===void 0?k4.maxEnqueuedMessages:e;this._messageQueue.length<s&&(this._debug("enqueue",l),this._messageQueue.push(l))}},c.prototype.addEventListener=function(l,e){this._listeners[l]&&this._listeners[l].push(e)},c.prototype.dispatchEvent=function(l){var e,s,t=this._listeners[l.type];if(t)try{for(var a=li(t),r=a.next();!r.done;r=a.next()){var i=r.value;this._callEventListener(l,i)}}catch(f){e={error:f}}finally{try{r&&!r.done&&(s=a.return)&&s.call(a)}finally{if(e)throw e.error}}return!0},c.prototype.removeEventListener=function(l,e){this._listeners[l]&&(this._listeners[l]=this._listeners[l].filter(function(s){return s!==e}))},c.prototype._debug=function(){for(var l,e=[],s=0;s<arguments.length;s++)e[s]=arguments[s];this._options.debug&&(l=this._logger).log.apply(l,si(["RWS>"],e))},c.prototype._getNextDelay=function(){var l=this._options,e=l.reconnectionDelayGrowFactor,s=e===void 0?k4.reconnectionDelayGrowFactor:e,t=l.minReconnectionDelay,a=t===void 0?k4.minReconnectionDelay:t,r=l.maxReconnectionDelay,i=r===void 0?k4.maxReconnectionDelay:r,f=0;return this._retryCount>0&&(f=a*Math.pow(s,this._retryCount-1),f>i&&(f=i)),this._debug("next delay",f),f},c.prototype._wait=function(){var l=this;return new Promise(function(e){setTimeout(e,l._getNextDelay())})},c.prototype._getNextUrl=function(l){if(typeof l=="string")return Promise.resolve(l);if(typeof l=="function"){var e=l();if(typeof e=="string")return Promise.resolve(e);if(e.then)return e}throw Error("Invalid URL")},c.prototype._connect=function(){var l=this;if(!(this._connectLock||!this._shouldReconnect)){this._connectLock=!0;var e=this._options,s=e.maxRetries,t=s===void 0?k4.maxRetries:s,a=e.connectionTimeout,r=a===void 0?k4.connectionTimeout:a,i=e.WebSocket,f=i===void 0?ai():i;if(this._retryCount>=t){this._debug("max retries reached",this._retryCount,">=",t);return}if(this._retryCount++,this._debug("connect",this._retryCount),this._removeListeners(),!ri(f))throw Error("No valid WebSocket class provided");this._wait().then(function(){return l._getNextUrl(l._url)}).then(function(M){if(l._closeCalled){l._connectLock=!1;return}l._debug("connect",{url:M,protocols:l._protocols,options:l._options}),l._ws=l._protocols?new f(M,l._protocols,l._options.WebSocketOptions):new f(M,l._options.WebSocketOptions),l._ws.binaryType=l._binaryType,l._connectLock=!1,l._addListeners(),l._connectTimeout=setTimeout(function(){return l._handleTimeout()},r)}).catch(function(M){l._connectLock=!1,l._handleError(new f7(Error(M.message),l))})}},c.prototype._handleTimeout=function(){this._debug("timeout event"),this._handleError(new f7(Error("TIMEOUT"),this))},c.prototype._disconnect=function(l,e){if(l===void 0&&(l=1e3),this._clearTimeouts(),!!this._ws){this._removeListeners();try{this._ws.close(l,e),this._handleClose(new ti(l,e,this))}catch{}}},c.prototype._acceptOpen=function(){this._debug("accept open"),this._retryCount=0},c.prototype._callEventListener=function(l,e){"handleEvent"in e?e.handleEvent(l):e(l)},c.prototype._removeListeners=function(){this._ws&&(this._debug("removeListeners"),this._ws.removeEventListener("open",this._handleOpen),this._ws.removeEventListener("close",this._handleClose),this._ws.removeEventListener("message",this._handleMessage),this._ws.removeEventListener("error",this._handleError))},c.prototype._addListeners=function(){this._ws&&(this._debug("addListeners"),this._ws.addEventListener("open",this._handleOpen),this._ws.addEventListener("close",this._handleClose),this._ws.addEventListener("message",this._handleMessage),this._ws.addEventListener("error",this._handleError))},c.prototype._clearTimeouts=function(){clearTimeout(this._connectTimeout),clearTimeout(this._uptimeTimeout)},c})();const ii={class:"app"},oi={class:"block"},fi={key:0,class:"cursor"},ui=P1({name:"app",components:{"spinner-grid":Ys},data:()=>({text:[],lastTextBlockKind:""}),async mounted(){
|
|
609
|
+
*/var ai=function(){if(typeof WebSocket<"u")return WebSocket},ri=function(c){return typeof c<"u"&&!!c&&c.CLOSING===2},k4={maxReconnectionDelay:1e4,minReconnectionDelay:1e3+Math.random()*4e3,minUptime:5e3,reconnectionDelayGrowFactor:1.3,connectionTimeout:4e3,maxRetries:1/0,maxEnqueuedMessages:1/0},ni=(function(){function c(l,e,s){var t=this;s===void 0&&(s={}),this._listeners={error:[],message:[],open:[],close:[]},this._retryCount=-1,this._shouldReconnect=!0,this._connectLock=!1,this._binaryType="blob",this._closeCalled=!1,this._messageQueue=[],this.onclose=null,this.onerror=null,this.onmessage=null,this.onopen=null,this._handleOpen=function(a){t._debug("open event");var r=t._options.minUptime,i=r===void 0?k4.minUptime:r;clearTimeout(t._connectTimeout),t._uptimeTimeout=setTimeout(function(){return t._acceptOpen()},i),t._ws.binaryType=t._binaryType,t._messageQueue.forEach(function(f){var M;return(M=t._ws)===null||M===void 0?void 0:M.send(f)}),t._messageQueue=[],t.onopen&&t.onopen(a),t._listeners.open.forEach(function(f){return t._callEventListener(a,f)})},this._handleMessage=function(a){t._debug("message event"),t.onmessage&&t.onmessage(a),t._listeners.message.forEach(function(r){return t._callEventListener(a,r)})},this._handleError=function(a){t._debug("error event",a.message),t._disconnect(void 0,a.message==="TIMEOUT"?"timeout":void 0),t.onerror&&t.onerror(a),t._debug("exec error listeners"),t._listeners.error.forEach(function(r){return t._callEventListener(a,r)}),t._connect()},this._handleClose=function(a){t._debug("close event"),t._clearTimeouts(),t._shouldReconnect&&t._connect(),t.onclose&&t.onclose(a),t._listeners.close.forEach(function(r){return t._callEventListener(a,r)})},this._url=l,this._protocols=e,this._options=s,this._options.startClosed&&(this._shouldReconnect=!1),s.logger?this._logger=s.logger:this._logger=console,this._connect()}return Object.defineProperty(c,"CONNECTING",{get:function(){return 0},enumerable:!0,configurable:!0}),Object.defineProperty(c,"OPEN",{get:function(){return 1},enumerable:!0,configurable:!0}),Object.defineProperty(c,"CLOSING",{get:function(){return 2},enumerable:!0,configurable:!0}),Object.defineProperty(c,"CLOSED",{get:function(){return 3},enumerable:!0,configurable:!0}),Object.defineProperty(c.prototype,"CONNECTING",{get:function(){return c.CONNECTING},enumerable:!0,configurable:!0}),Object.defineProperty(c.prototype,"OPEN",{get:function(){return c.OPEN},enumerable:!0,configurable:!0}),Object.defineProperty(c.prototype,"CLOSING",{get:function(){return c.CLOSING},enumerable:!0,configurable:!0}),Object.defineProperty(c.prototype,"CLOSED",{get:function(){return c.CLOSED},enumerable:!0,configurable:!0}),Object.defineProperty(c.prototype,"binaryType",{get:function(){return this._ws?this._ws.binaryType:this._binaryType},set:function(l){this._binaryType=l,this._ws&&(this._ws.binaryType=l)},enumerable:!0,configurable:!0}),Object.defineProperty(c.prototype,"retryCount",{get:function(){return Math.max(this._retryCount,0)},enumerable:!0,configurable:!0}),Object.defineProperty(c.prototype,"bufferedAmount",{get:function(){var l=this._messageQueue.reduce(function(e,s){return typeof s=="string"?e+=s.length:s instanceof Blob?e+=s.size:e+=s.byteLength,e},0);return l+(this._ws?this._ws.bufferedAmount:0)},enumerable:!0,configurable:!0}),Object.defineProperty(c.prototype,"extensions",{get:function(){return this._ws?this._ws.extensions:""},enumerable:!0,configurable:!0}),Object.defineProperty(c.prototype,"protocol",{get:function(){return this._ws?this._ws.protocol:""},enumerable:!0,configurable:!0}),Object.defineProperty(c.prototype,"readyState",{get:function(){return this._ws?this._ws.readyState:this._options.startClosed?c.CLOSED:c.CONNECTING},enumerable:!0,configurable:!0}),Object.defineProperty(c.prototype,"url",{get:function(){return this._ws?this._ws.url:""},enumerable:!0,configurable:!0}),c.prototype.close=function(l,e){if(l===void 0&&(l=1e3),this._closeCalled=!0,this._shouldReconnect=!1,this._clearTimeouts(),!this._ws){this._debug("close enqueued: no ws instance");return}if(this._ws.readyState===this.CLOSED){this._debug("close: already closed");return}this._ws.close(l,e)},c.prototype.reconnect=function(l,e){this._shouldReconnect=!0,this._closeCalled=!1,this._retryCount=-1,!this._ws||this._ws.readyState===this.CLOSED?this._connect():(this._disconnect(l,e),this._connect())},c.prototype.send=function(l){if(this._ws&&this._ws.readyState===this.OPEN)this._debug("send",l),this._ws.send(l);else{var e=this._options.maxEnqueuedMessages,s=e===void 0?k4.maxEnqueuedMessages:e;this._messageQueue.length<s&&(this._debug("enqueue",l),this._messageQueue.push(l))}},c.prototype.addEventListener=function(l,e){this._listeners[l]&&this._listeners[l].push(e)},c.prototype.dispatchEvent=function(l){var e,s,t=this._listeners[l.type];if(t)try{for(var a=li(t),r=a.next();!r.done;r=a.next()){var i=r.value;this._callEventListener(l,i)}}catch(f){e={error:f}}finally{try{r&&!r.done&&(s=a.return)&&s.call(a)}finally{if(e)throw e.error}}return!0},c.prototype.removeEventListener=function(l,e){this._listeners[l]&&(this._listeners[l]=this._listeners[l].filter(function(s){return s!==e}))},c.prototype._debug=function(){for(var l,e=[],s=0;s<arguments.length;s++)e[s]=arguments[s];this._options.debug&&(l=this._logger).log.apply(l,si(["RWS>"],e))},c.prototype._getNextDelay=function(){var l=this._options,e=l.reconnectionDelayGrowFactor,s=e===void 0?k4.reconnectionDelayGrowFactor:e,t=l.minReconnectionDelay,a=t===void 0?k4.minReconnectionDelay:t,r=l.maxReconnectionDelay,i=r===void 0?k4.maxReconnectionDelay:r,f=0;return this._retryCount>0&&(f=a*Math.pow(s,this._retryCount-1),f>i&&(f=i)),this._debug("next delay",f),f},c.prototype._wait=function(){var l=this;return new Promise(function(e){setTimeout(e,l._getNextDelay())})},c.prototype._getNextUrl=function(l){if(typeof l=="string")return Promise.resolve(l);if(typeof l=="function"){var e=l();if(typeof e=="string")return Promise.resolve(e);if(e.then)return e}throw Error("Invalid URL")},c.prototype._connect=function(){var l=this;if(!(this._connectLock||!this._shouldReconnect)){this._connectLock=!0;var e=this._options,s=e.maxRetries,t=s===void 0?k4.maxRetries:s,a=e.connectionTimeout,r=a===void 0?k4.connectionTimeout:a,i=e.WebSocket,f=i===void 0?ai():i;if(this._retryCount>=t){this._debug("max retries reached",this._retryCount,">=",t);return}if(this._retryCount++,this._debug("connect",this._retryCount),this._removeListeners(),!ri(f))throw Error("No valid WebSocket class provided");this._wait().then(function(){return l._getNextUrl(l._url)}).then(function(M){if(l._closeCalled){l._connectLock=!1;return}l._debug("connect",{url:M,protocols:l._protocols,options:l._options}),l._ws=l._protocols?new f(M,l._protocols,l._options.WebSocketOptions):new f(M,l._options.WebSocketOptions),l._ws.binaryType=l._binaryType,l._connectLock=!1,l._addListeners(),l._connectTimeout=setTimeout(function(){return l._handleTimeout()},r)}).catch(function(M){l._connectLock=!1,l._handleError(new f7(Error(M.message),l))})}},c.prototype._handleTimeout=function(){this._debug("timeout event"),this._handleError(new f7(Error("TIMEOUT"),this))},c.prototype._disconnect=function(l,e){if(l===void 0&&(l=1e3),this._clearTimeouts(),!!this._ws){this._removeListeners();try{this._ws.close(l,e),this._handleClose(new ti(l,e,this))}catch{}}},c.prototype._acceptOpen=function(){this._debug("accept open"),this._retryCount=0},c.prototype._callEventListener=function(l,e){"handleEvent"in e?e.handleEvent(l):e(l)},c.prototype._removeListeners=function(){this._ws&&(this._debug("removeListeners"),this._ws.removeEventListener("open",this._handleOpen),this._ws.removeEventListener("close",this._handleClose),this._ws.removeEventListener("message",this._handleMessage),this._ws.removeEventListener("error",this._handleError))},c.prototype._addListeners=function(){this._ws&&(this._debug("addListeners"),this._ws.addEventListener("open",this._handleOpen),this._ws.addEventListener("close",this._handleClose),this._ws.addEventListener("message",this._handleMessage),this._ws.addEventListener("error",this._handleError))},c.prototype._clearTimeouts=function(){clearTimeout(this._connectTimeout),clearTimeout(this._uptimeTimeout)},c})();const ii={class:"app"},oi={class:"block"},fi={key:0,class:"cursor"},ui=P1({name:"app",components:{"spinner-grid":Ys},data:()=>({text:[],lastTextBlockKind:""}),async mounted(){const c=new URL("/api",document.location.href).toString();new ni(c,[],{reconnectionDelayGrowFactor:1.3,maxReconnectionDelay:4e3,minReconnectionDelay:1e3,connectionTimeout:4e3,minUptime:5e3}).addEventListener("message",e=>{let s;try{s=JSON.parse(e.data)}catch(t){this.log("ERROR","Failed to parse WebSocket message",{error:t,data:e.data});return}this.lastTextBlockKind==="intermediate"?this.text[this.text.length-1]=s.payload:(this.text.push(s.payload),this.text=this.text.slice(-2)),this.lastTextBlockKind=s.kind})},methods:{log(c,l,e=null){let t=`${H().format("YYYY-MM-DD hh:mm:ss.SSS")} [${c}]: ${l}`;e!==null&&(t+=` (${Object.keys(e).map(a=>a+": "+JSON.stringify(e[a])).join(", ")})`),console.log(t)}}}),zi=P1({...ui,setup(c){return(l,e)=>{const s=Gl("spinner-grid");return p3(),X3("div",ii,[b0("div",oi,[b0("div",{class:A0(["text-col",{intermediate:l.lastTextBlockKind==="intermediate"}])},[(p3(!0),X3(p2,null,Zl(l.text,(t,a)=>(p3(),X3("div",{key:t,class:"text-value"},[m9(p7(t)+" ",1),a===l.text.length-1&&l.lastTextBlockKind==="intermediate"?(p3(),X3("span",fi,[S2(s,{class:"spinner-grid",size:"32"})])):Oe("",!0)]))),128))],2)])])}}});document.addEventListener("DOMContentLoaded",c=>{(async()=>{us(zi).mount("#app")})().catch(l=>{console.error(`app: ERROR: top-level: ${l}`)})})});export default mi();
|
|
@@ -4,13 +4,11 @@
|
|
|
4
4
|
** Licensed under GPL 3.0 <https://spdx.org/licenses/GPL-3.0-only>
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import path from "node:path"
|
|
8
7
|
import * as Vite from "vite"
|
|
9
8
|
import VuePlugin from "@vitejs/plugin-vue"
|
|
10
9
|
import YAMLPlugin from "@rollup/plugin-yaml"
|
|
11
10
|
import { nodePolyfills } from "vite-plugin-node-polyfills"
|
|
12
11
|
import SvgLoader from "vite-svg-loader"
|
|
13
|
-
import { mkdirp } from "mkdirp"
|
|
14
12
|
|
|
15
13
|
export default Vite.defineConfig(({ command, mode }) => ({
|
|
16
14
|
logLevel: "info",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
<div v-bind:key="value"
|
|
15
15
|
v-for="(value, idx) of text"
|
|
16
16
|
class="text-value">
|
|
17
|
-
{{ value
|
|
17
|
+
{{ value }}
|
|
18
18
|
<span class="cursor" v-if="idx === (text.length - 1) && lastTextBlockKind === 'intermediate'">
|
|
19
19
|
<spinner-grid class="spinner-grid" size="32"/>
|
|
20
20
|
</span>
|
|
@@ -94,10 +94,7 @@ export default defineComponent({
|
|
|
94
94
|
}),
|
|
95
95
|
async mounted () {
|
|
96
96
|
/* determine API URL */
|
|
97
|
-
|
|
98
|
-
url = url.replace(/#.+$/, "")
|
|
99
|
-
url = url.replace(/\/[^/]*$/, "")
|
|
100
|
-
url = url + "/api"
|
|
97
|
+
const url = new URL("/api", document.location.href).toString()
|
|
101
98
|
|
|
102
99
|
/* connect to WebSocket API for receiving dashboard information */
|
|
103
100
|
const ws = new ReconnectingWebSocket(url, [], {
|
|
@@ -108,7 +105,14 @@ export default defineComponent({
|
|
|
108
105
|
minUptime: 5000
|
|
109
106
|
})
|
|
110
107
|
ws.addEventListener("message", (ev) => {
|
|
111
|
-
|
|
108
|
+
let chunk: SpeechFlowChunk
|
|
109
|
+
try {
|
|
110
|
+
chunk = JSON.parse(ev.data)
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
this.log("ERROR", "Failed to parse WebSocket message", { error, data: ev.data })
|
|
114
|
+
return
|
|
115
|
+
}
|
|
112
116
|
if (this.lastTextBlockKind === "intermediate")
|
|
113
117
|
this.text[this.text.length - 1] = chunk.payload
|
|
114
118
|
else {
|