mirror of
https://github.com/steveseguin/caption-local.git
synced 2026-10-10 01:59:31 +00:00
45 lines
2.5 KiB
JavaScript
45 lines
2.5 KiB
JavaScript
'use strict';
|
|
// No credentials, rooms, audio, or captions are persisted by this transport.
|
|
class LocalConnection {
|
|
constructor() { this.connected = false; this.busy = false; this.endpoint = ''; this.token = ''; this.metrics = []; }
|
|
configure(endpoint, token, pageOrigin = location.origin) {
|
|
if (this.busy) throw new Error('Stop and finish or discard pending audio before changing the connection.');
|
|
const url = new URL(endpoint);
|
|
if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password ||
|
|
url.search || url.hash || url.pathname !== '/' ||
|
|
(url.protocol === 'http:' && !['localhost', '127.0.0.1', '[::1]'].includes(url.hostname))) {
|
|
throw new Error('Use an HTTPS service origin or http://localhost:8765, without a path or credentials.');
|
|
}
|
|
if (url.origin !== pageOrigin && !token) throw new Error('A service token is required for a different origin.');
|
|
this.endpoint = url.origin; this.token = token; this.connected = true; this.metrics = []; this.lastInference = null;
|
|
this.revision = (this.revision || 0) + 1;
|
|
}
|
|
setBusy(busy) {
|
|
this.busy = busy;
|
|
for (const id of ['endpoint', 'connectionToken', 'connect', 'loadSettings']) {
|
|
const control = document.getElementById(id); if (control) control.disabled = busy || this.checking;
|
|
}
|
|
}
|
|
async fetch(path, options = {}) {
|
|
if (!this.connected) throw new Error('Connect to your service first.');
|
|
// Reject absolute/protocol-relative URLs so authorization never escapes the selected host.
|
|
if (!/^\/(health|transcribe(?:\?|$)|streams\/)/.test(path) || path.startsWith('//')) throw new Error('Unsupported service route.');
|
|
const headers = new Headers(options.headers || {});
|
|
if (this.token) headers.set('Authorization', `Bearer ${this.token}`);
|
|
const start = performance.now(), revision = this.revision; let status = 0;
|
|
try {
|
|
const response = await fetch(this.endpoint + path, {...options, headers,
|
|
redirect: 'error', credentials: 'omit', referrerPolicy: 'no-referrer'});
|
|
status = response.status; return response;
|
|
} finally {
|
|
if (revision === this.revision) {
|
|
this.metrics.push({route: path.split('?')[0].startsWith('/streams/') ? '/streams/:id' : path.split('?')[0],
|
|
status, milliseconds: Math.round(performance.now() - start)});
|
|
if (this.metrics.length > 100) this.metrics.shift();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (typeof module !== 'undefined') module.exports = {LocalConnection};
|
|
else window.captionLocalConnection = new LocalConnection();
|