@vdegenne/kokoro-proxy 0.0.0-stage → 0.1.2

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/lib/cache.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ import { Readable } from 'node:stream';
2
+ import { type SpeechOptions } from './hash.js';
3
+ export interface CachedAudio {
4
+ body: Buffer | Readable;
5
+ size: number;
6
+ }
7
+ export declare function getSpeech(options: SpeechOptions): Promise<CachedAudio>;
8
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAIA,OAAO,EAAC,QAAQ,EAAC,MAAM,aAAa,CAAA;AAIpC,OAAO,EAAc,KAAK,aAAa,EAAC,MAAM,WAAW,CAAA;AAGzD,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAA;IACvB,IAAI,EAAE,MAAM,CAAA;CACZ;AA+ID,wBAAsB,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,WAAW,CAAC,CAsD5E"}
package/lib/cache.js ADDED
@@ -0,0 +1,148 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { createReadStream, createWriteStream } from 'node:fs';
3
+ import { mkdir, readFile, rename, stat, unlink } from 'node:fs/promises';
4
+ import { join } from 'node:path';
5
+ import { pipeline } from 'node:stream/promises';
6
+ import { config } from './config.js';
7
+ import { getCacheKey } from './hash.js';
8
+ import { recordUsage } from './stats.js';
9
+ const memoryCache = new Map();
10
+ const inFlight = new Map();
11
+ let memoryCacheSize = 0;
12
+ function getDiskPath(hash) {
13
+ return join(config.cacheDir, 'audio', hash.slice(0, 2), hash.slice(2, 4), `${hash}.mp3`);
14
+ }
15
+ function getMemory(hash) {
16
+ const entry = memoryCache.get(hash);
17
+ if (!entry)
18
+ return undefined;
19
+ // Refresh insertion order to implement LRU.
20
+ memoryCache.delete(hash);
21
+ memoryCache.set(hash, entry);
22
+ return entry.data;
23
+ }
24
+ function setMemory(hash, data) {
25
+ if (data.length > config.memoryCacheMaxBytes)
26
+ return;
27
+ const previous = memoryCache.get(hash);
28
+ if (previous) {
29
+ memoryCacheSize -= previous.data.length;
30
+ memoryCache.delete(hash);
31
+ }
32
+ while (memoryCacheSize + data.length > config.memoryCacheMaxBytes) {
33
+ const oldest = memoryCache.keys().next().value;
34
+ if (oldest === undefined)
35
+ break;
36
+ const entry = memoryCache.get(oldest);
37
+ memoryCacheSize -= entry.data.length;
38
+ memoryCache.delete(oldest);
39
+ }
40
+ memoryCache.set(hash, { data });
41
+ memoryCacheSize += data.length;
42
+ }
43
+ async function getDiskAudio(hash) {
44
+ const path = getDiskPath(hash);
45
+ let fileSize;
46
+ try {
47
+ fileSize = (await stat(path)).size;
48
+ }
49
+ catch (error) {
50
+ if (error.code === 'ENOENT') {
51
+ return undefined;
52
+ }
53
+ throw error;
54
+ }
55
+ if (fileSize <= config.memoryCacheMaxBytes) {
56
+ const data = await readFile(path);
57
+ setMemory(hash, data);
58
+ return { body: data, size: data.length };
59
+ }
60
+ return {
61
+ body: createReadStream(path),
62
+ size: fileSize,
63
+ };
64
+ }
65
+ async function synthesize(options, hash) {
66
+ const path = getDiskPath(hash);
67
+ const directory = join(path, '..');
68
+ const tempPath = `${path}.${randomUUID()}.tmp`;
69
+ await mkdir(directory, { recursive: true });
70
+ try {
71
+ const response = await fetch(`${config.upstreamUrl}/v1/audio/speech`, {
72
+ method: 'POST',
73
+ headers: {
74
+ 'content-type': 'application/json',
75
+ },
76
+ body: JSON.stringify({
77
+ model: 'tts-1',
78
+ input: options.input,
79
+ voice: options.voice,
80
+ speed: options.speed,
81
+ volume_multiplier: options.volume_multiplier,
82
+ response_format: 'mp3',
83
+ }),
84
+ });
85
+ if (!response.ok) {
86
+ const details = await response.text().catch(() => '');
87
+ throw new Error(`Kokoro returned HTTP ${response.status}` +
88
+ (details ? `: ${details}` : ''));
89
+ }
90
+ if (!response.body) {
91
+ throw new Error('Kokoro returned an empty response body');
92
+ }
93
+ await pipeline(response.body, createWriteStream(tempPath, { flags: 'wx' }));
94
+ await rename(tempPath, path);
95
+ const fileSize = (await stat(path)).size;
96
+ if (fileSize <= config.memoryCacheMaxBytes) {
97
+ try {
98
+ setMemory(hash, await readFile(path));
99
+ }
100
+ catch {
101
+ // The disk cache remains usable if memory caching fails.
102
+ }
103
+ }
104
+ }
105
+ finally {
106
+ await unlink(tempPath).catch(() => { });
107
+ }
108
+ }
109
+ export async function getSpeech(options) {
110
+ const hash = getCacheKey(options);
111
+ let audio = getMemory(hash);
112
+ if (audio) {
113
+ recordUsage(hash);
114
+ return { body: audio, size: audio.length };
115
+ }
116
+ const cached = await getDiskAudio(hash);
117
+ if (cached) {
118
+ recordUsage(hash);
119
+ return cached;
120
+ }
121
+ let pending = inFlight.get(hash);
122
+ if (!pending) {
123
+ pending = synthesize(options, hash);
124
+ inFlight.set(hash, pending);
125
+ void pending.then(() => {
126
+ if (inFlight.get(hash) === pending) {
127
+ inFlight.delete(hash);
128
+ }
129
+ }, () => {
130
+ if (inFlight.get(hash) === pending) {
131
+ inFlight.delete(hash);
132
+ }
133
+ });
134
+ }
135
+ await pending;
136
+ audio = getMemory(hash);
137
+ if (audio) {
138
+ recordUsage(hash);
139
+ return { body: audio, size: audio.length };
140
+ }
141
+ const result = await getDiskAudio(hash);
142
+ if (!result) {
143
+ throw new Error('Audio was generated but is missing from the disk cache');
144
+ }
145
+ recordUsage(hash);
146
+ return result;
147
+ }
148
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=clean-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clean-cache.d.ts","sourceRoot":"","sources":["../src/clean-cache.ts"],"names":[],"mappings":""}
@@ -0,0 +1,95 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { mkdir, readFile, rename, stat, unlink, writeFile, } from 'node:fs/promises';
3
+ import { dirname, join } from 'node:path';
4
+ import { config } from './config.js';
5
+ const gib = 1024 ** 3;
6
+ const maxBytes = Number(process.env.DISK_CACHE_MAX_BYTES ?? 5 * gib);
7
+ if (!Number.isSafeInteger(maxBytes) || maxBytes <= 0) {
8
+ throw new Error('DISK_CACHE_MAX_BYTES must be a positive integer');
9
+ }
10
+ const audioDir = join(config.cacheDir, 'audio');
11
+ const statsPath = join(config.cacheDir, 'stats.json');
12
+ function getAudioPath(hash) {
13
+ return join(audioDir, hash.slice(0, 2), hash.slice(2, 4), `${hash}.mp3`);
14
+ }
15
+ async function main() {
16
+ let stats;
17
+ try {
18
+ const content = await readFile(statsPath, 'utf8');
19
+ const parsed = JSON.parse(content);
20
+ if (parsed === null ||
21
+ typeof parsed !== 'object' ||
22
+ Array.isArray(parsed)) {
23
+ throw new Error('Invalid stats.json format');
24
+ }
25
+ stats = parsed;
26
+ }
27
+ catch (error) {
28
+ if (error.code === 'ENOENT') {
29
+ console.log('No stats.json found. Nothing to clean.');
30
+ return;
31
+ }
32
+ throw error;
33
+ }
34
+ const entries = Object.entries(stats).filter(([hash, count]) => /^[a-f0-9]{64}$/.test(hash) && Number.isSafeInteger(count) && count >= 0);
35
+ const files = [];
36
+ let totalBytes = 0;
37
+ for (const [hash, uses] of entries) {
38
+ const path = getAudioPath(hash);
39
+ try {
40
+ const info = await stat(path);
41
+ if (!info.isFile())
42
+ continue;
43
+ files.push({
44
+ hash,
45
+ path,
46
+ size: info.size,
47
+ uses,
48
+ mtimeMs: info.mtimeMs,
49
+ });
50
+ totalBytes += info.size;
51
+ }
52
+ catch (error) {
53
+ if (error.code !== 'ENOENT') {
54
+ throw error;
55
+ }
56
+ // Remove stale statistics for files that no longer exist.
57
+ delete stats[hash];
58
+ }
59
+ }
60
+ const initialBytes = totalBytes;
61
+ // Least-used files first; oldest files break ties.
62
+ files.sort((a, b) => a.uses - b.uses || a.mtimeMs - b.mtimeMs);
63
+ let deletedFiles = 0;
64
+ for (const file of files) {
65
+ if (totalBytes <= maxBytes)
66
+ break;
67
+ await unlink(file.path);
68
+ totalBytes -= file.size;
69
+ delete stats[file.hash];
70
+ deletedFiles++;
71
+ console.log(`Deleted ${file.hash}.mp3 (${file.size} bytes, ${file.uses} uses)`);
72
+ }
73
+ if (deletedFiles > 0 || files.length !== entries.length) {
74
+ await mkdir(dirname(statsPath), { recursive: true });
75
+ const tempPath = `${statsPath}.${randomUUID()}.tmp`;
76
+ try {
77
+ await writeFile(tempPath, JSON.stringify(stats), 'utf8');
78
+ await rename(tempPath, statsPath);
79
+ }
80
+ finally {
81
+ await unlink(tempPath).catch(() => { });
82
+ }
83
+ }
84
+ console.log(`Cache: ${(initialBytes / gib).toFixed(2)} GiB → ` +
85
+ `${(totalBytes / gib).toFixed(2)} GiB`);
86
+ console.log(`Deleted ${deletedFiles} file(s).`);
87
+ if (totalBytes > maxBytes) {
88
+ console.log('Cache remains above the limit. Some audio files may not be tracked in stats.json.');
89
+ }
90
+ }
91
+ main().catch((error) => {
92
+ console.error('Cache cleanup failed:', error);
93
+ process.exitCode = 1;
94
+ });
95
+ //# sourceMappingURL=clean-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clean-cache.js","sourceRoot":"","sources":["../src/clean-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export declare const config: {
2
+ readonly host: string;
3
+ readonly port: number;
4
+ readonly upstreamUrl: string;
5
+ readonly cacheDir: string;
6
+ readonly memoryCacheMaxBytes: number;
7
+ readonly statsFlushIntervalMs: number;
8
+ };
9
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AA4BA,eAAO,MAAM,MAAM;aAClB,IAAI;aACJ,IAAI;aAEJ,WAAW;aAEX,QAAQ;aAER,mBAAmB;aAEnB,oBAAoB;CACX,CAAA"}
package/lib/config.js ADDED
@@ -0,0 +1,30 @@
1
+ import { resolve } from 'node:path';
2
+ function getPositiveInteger(name, fallback) {
3
+ const value = process.env[name];
4
+ if (value === undefined)
5
+ return fallback;
6
+ const parsed = Number(value);
7
+ if (!Number.isSafeInteger(parsed) || parsed <= 0) {
8
+ throw new Error(`${name} must be a positive integer`);
9
+ }
10
+ return parsed;
11
+ }
12
+ function getUrl(name, fallback) {
13
+ const value = process.env[name] ?? fallback;
14
+ try {
15
+ return new URL(value).toString().replace(/\/$/, '');
16
+ }
17
+ catch {
18
+ throw new Error(`${name} must be a valid URL`);
19
+ }
20
+ }
21
+ const mib = 1024 * 1024;
22
+ export const config = {
23
+ host: process.env.HOST ?? '127.0.0.1',
24
+ port: getPositiveInteger('PORT', 3000),
25
+ upstreamUrl: getUrl('KOKORO_URL', 'http://127.0.0.1:8880'),
26
+ cacheDir: resolve(process.env.CACHE_DIR ?? 'data'),
27
+ memoryCacheMaxBytes: getPositiveInteger('MEMORY_CACHE_MAX_BYTES', 100 * mib),
28
+ statsFlushIntervalMs: getPositiveInteger('STATS_FLUSH_INTERVAL_MS', 30_000),
29
+ };
30
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AAEjC,SAAS,kBAAkB,CAAC,IAAY,EAAE,QAAgB;IACzD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAE/B,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAA;IAExC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;IAE5B,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,6BAA6B,CAAC,CAAA;IACtD,CAAC;IAED,OAAO,MAAM,CAAA;AACd,CAAC;AAED,SAAS,MAAM,CAAC,IAAY,EAAE,QAAgB;IAC7C,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAA;IAE3C,IAAI,CAAC;QACJ,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IACpD,CAAC;IAAC,MAAM,CAAC;QACR,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,sBAAsB,CAAC,CAAA;IAC/C,CAAC;AACF,CAAC;AAED,MAAM,GAAG,GAAG,IAAI,GAAG,IAAI,CAAA;AAEvB,MAAM,CAAC,MAAM,MAAM,GAAG;IACrB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,WAAW;IACrC,IAAI,EAAE,kBAAkB,CAAC,MAAM,EAAE,IAAI,CAAC;IAEtC,WAAW,EAAE,MAAM,CAAC,YAAY,EAAE,uBAAuB,CAAC;IAE1D,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,MAAM,CAAC;IAElD,mBAAmB,EAAE,kBAAkB,CAAC,wBAAwB,EAAE,GAAG,GAAG,GAAG,CAAC;IAE5E,oBAAoB,EAAE,kBAAkB,CAAC,yBAAyB,EAAE,MAAM,CAAC;CAClE,CAAA"}
package/lib/core.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { getCacheKey, type SpeechOptions } from './hash.js';
2
+ //# sourceMappingURL=core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAE,KAAK,aAAa,EAAC,MAAM,WAAW,CAAA"}
package/lib/core.js ADDED
@@ -0,0 +1,2 @@
1
+ export { getCacheKey } from './hash.js';
2
+ //# sourceMappingURL=core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAqB,MAAM,WAAW,CAAA"}
package/lib/hash.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ export interface SpeechOptions {
2
+ input: string;
3
+ voice: string;
4
+ speed: number;
5
+ volume_multiplier: number;
6
+ }
7
+ export declare function getCacheKey(options: SpeechOptions): string;
8
+ //# sourceMappingURL=hash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../src/hash.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,iBAAiB,EAAE,MAAM,CAAA;CACzB;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAS1D"}
package/lib/hash.js ADDED
@@ -0,0 +1,11 @@
1
+ import { createHash } from 'node:crypto';
2
+ export function getCacheKey(options) {
3
+ const payload = JSON.stringify([
4
+ options.input,
5
+ options.voice,
6
+ options.speed,
7
+ options.volume_multiplier,
8
+ ]);
9
+ return createHash('sha256').update(payload).digest('hex');
10
+ }
11
+ //# sourceMappingURL=hash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.js","sourceRoot":"","sources":["../src/hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,aAAa,CAAA;AAStC,MAAM,UAAU,WAAW,CAAC,OAAsB;IACjD,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC;QAC9B,OAAO,CAAC,KAAK;QACb,OAAO,CAAC,KAAK;QACb,OAAO,CAAC,KAAK;QACb,OAAO,CAAC,iBAAiB;KACzB,CAAC,CAAA;IAEF,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAC1D,CAAC"}
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { Hono } from 'hono';
3
+ declare const app: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
4
+ export { app };
5
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AAEA,OAAO,EAAC,IAAI,EAAC,MAAM,MAAM,CAAA;AAQzB,QAAA,MAAM,GAAG,4EAAa,CAAA;AAkNtB,OAAO,EAAC,GAAG,EAAC,CAAA"}
package/lib/server.js ADDED
@@ -0,0 +1,175 @@
1
+ #!/usr/bin/env node
2
+ import { serve } from '@hono/node-server';
3
+ import { Hono } from 'hono';
4
+ import { cors } from 'hono/cors';
5
+ import { Readable } from 'node:stream';
6
+ import { getSpeech } from './cache.js';
7
+ import { config } from './config.js';
8
+ import { closeStats } from './stats.js';
9
+ const app = new Hono();
10
+ app.use('*', cors({
11
+ origin: [
12
+ 'http://localhost:35107',
13
+ 'https://glaizme.web.app',
14
+ 'https://gl-ai-z.win',
15
+ ],
16
+ allowMethods: ['GET', 'POST', 'OPTIONS'],
17
+ allowHeaders: ['Content-Type', 'Authorization'],
18
+ }));
19
+ app.get('/health', async (c) => {
20
+ try {
21
+ const response = await fetch(`${config.upstreamUrl}/health`, {
22
+ signal: AbortSignal.timeout(3000),
23
+ });
24
+ return new Response(response.body, {
25
+ status: response.status,
26
+ headers: {
27
+ 'content-type': response.headers.get('content-type') ?? 'application/json',
28
+ },
29
+ });
30
+ }
31
+ catch {
32
+ return c.json({ status: 'unhealthy' }, 503);
33
+ }
34
+ });
35
+ app.get('/v1/voices', async (c) => {
36
+ try {
37
+ const response = await fetch(`${config.upstreamUrl}/v1/voices`, {
38
+ signal: AbortSignal.timeout(10_000),
39
+ });
40
+ const body = await response.arrayBuffer();
41
+ return new Response(body, {
42
+ status: response.status,
43
+ headers: {
44
+ 'content-type': response.headers.get('content-type') ?? 'application/json',
45
+ },
46
+ });
47
+ }
48
+ catch {
49
+ return c.json({ error: 'Kokoro server unavailable' }, 502);
50
+ }
51
+ });
52
+ app.get('/v1/audio/speech', async (c) => {
53
+ const input = c.req.query('input');
54
+ const voice = c.req.query('voice');
55
+ const speed = Number(c.req.query('speed') ?? 1);
56
+ const volumeMultiplier = Number(c.req.query('volume_multiplier') ?? 1);
57
+ if (!input?.trim() || !voice?.trim()) {
58
+ return c.json({ error: 'input and voice must be non-empty strings' }, 400);
59
+ }
60
+ if (!Number.isFinite(speed) ||
61
+ speed <= 0 ||
62
+ !Number.isFinite(volumeMultiplier) ||
63
+ volumeMultiplier <= 0) {
64
+ return c.json({ error: 'speed and volume_multiplier must be positive numbers' }, 400);
65
+ }
66
+ try {
67
+ const audio = await getSpeech({
68
+ input,
69
+ voice,
70
+ speed,
71
+ volume_multiplier: volumeMultiplier,
72
+ });
73
+ const stream = audio.body instanceof Readable
74
+ ? Readable.toWeb(audio.body)
75
+ : Readable.toWeb(Readable.from([audio.body]));
76
+ return new Response(stream, {
77
+ headers: {
78
+ 'content-type': 'audio/mpeg',
79
+ 'content-length': String(audio.size),
80
+ // 'cache-control': 'private, max-age=86400',
81
+ 'cache-control': 'private, max-age=31536000, immutable',
82
+ },
83
+ });
84
+ }
85
+ catch (error) {
86
+ console.error('Speech synthesis failed:', error);
87
+ return c.json({ error: 'Speech synthesis failed' }, 502);
88
+ }
89
+ });
90
+ app.post('/v1/audio/speech', async (c) => {
91
+ let body;
92
+ try {
93
+ body = await c.req.json();
94
+ }
95
+ catch {
96
+ return c.json({ error: 'Invalid JSON body' }, 400);
97
+ }
98
+ if (body === null || typeof body !== 'object' || Array.isArray(body)) {
99
+ return c.json({ error: 'Expected a JSON object' }, 400);
100
+ }
101
+ const input = body;
102
+ if (typeof input.input !== 'string' ||
103
+ input.input.trim().length === 0 ||
104
+ typeof input.voice !== 'string' ||
105
+ input.voice.trim().length === 0) {
106
+ return c.json({ error: 'input and voice must be non-empty strings' }, 400);
107
+ }
108
+ const speed = input.speed ?? 1;
109
+ const volumeMultiplier = input.volume_multiplier ?? 1;
110
+ if (typeof speed !== 'number' ||
111
+ !Number.isFinite(speed) ||
112
+ speed <= 0 ||
113
+ typeof volumeMultiplier !== 'number' ||
114
+ !Number.isFinite(volumeMultiplier) ||
115
+ volumeMultiplier <= 0) {
116
+ return c.json({ error: 'speed and volume_multiplier must be positive numbers' }, 400);
117
+ }
118
+ try {
119
+ const audio = await getSpeech({
120
+ input: input.input,
121
+ voice: input.voice,
122
+ speed,
123
+ volume_multiplier: volumeMultiplier,
124
+ });
125
+ const stream = audio.body instanceof Readable
126
+ ? Readable.toWeb(audio.body)
127
+ : Readable.toWeb(Readable.from([audio.body]));
128
+ return new Response(stream, {
129
+ headers: {
130
+ 'content-type': 'audio/mpeg',
131
+ 'content-length': String(audio.size),
132
+ 'cache-control': 'no-store',
133
+ },
134
+ });
135
+ }
136
+ catch (error) {
137
+ console.error('Speech synthesis failed:', error);
138
+ return c.json({ error: 'Speech synthesis failed' }, 502);
139
+ }
140
+ });
141
+ const server = serve({
142
+ fetch: app.fetch,
143
+ hostname: config.host,
144
+ port: config.port,
145
+ });
146
+ console.log(`
147
+ ##############################
148
+ # Kokoro Proxy #
149
+ ##############################
150
+ # Server: http://${config.host}:${config.port}
151
+ # Cache: ${config.cacheDir}
152
+ # Upstream: ${config.upstreamUrl}
153
+ ##############################
154
+ `);
155
+ let shuttingDown = false;
156
+ async function shutdown(signal) {
157
+ if (shuttingDown)
158
+ return;
159
+ shuttingDown = true;
160
+ console.log(`\nReceived ${signal}. Shutting down Kokoro Proxy...`);
161
+ server.close();
162
+ try {
163
+ await closeStats();
164
+ console.log('Usage stats flushed successfully.');
165
+ }
166
+ catch (error) {
167
+ console.error('Failed to flush usage stats:', error);
168
+ process.exitCode = 1;
169
+ }
170
+ console.log('Kokoro Proxy stopped.');
171
+ }
172
+ process.on('SIGINT', () => void shutdown('SIGINT'));
173
+ process.on('SIGTERM', () => void shutdown('SIGTERM'));
174
+ export { app };
175
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/lib/stats.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export declare function recordUsage(hash: string): void;
2
+ export declare function closeStats(): Promise<void>;
3
+ //# sourceMappingURL=stats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../src/stats.ts"],"names":[],"mappings":"AAmCA,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE9C;AAsDD,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAQhD"}
package/lib/stats.js ADDED
@@ -0,0 +1,80 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
3
+ import { dirname, join } from 'node:path';
4
+ import { config } from './config.js';
5
+ const statsPath = join(config.cacheDir, 'stats.json');
6
+ const pending = {};
7
+ let flushPromise;
8
+ async function readStats() {
9
+ try {
10
+ const content = await readFile(statsPath, 'utf8');
11
+ const parsed = JSON.parse(content);
12
+ if (parsed === null ||
13
+ typeof parsed !== 'object' ||
14
+ Array.isArray(parsed)) {
15
+ throw new Error('Invalid stats.json format');
16
+ }
17
+ return parsed;
18
+ }
19
+ catch (error) {
20
+ if (error.code === 'ENOENT') {
21
+ return {};
22
+ }
23
+ throw error;
24
+ }
25
+ }
26
+ export function recordUsage(hash) {
27
+ pending[hash] = (pending[hash] ?? 0) + 1;
28
+ }
29
+ async function flushStats() {
30
+ const snapshot = { ...pending };
31
+ for (const hash of Object.keys(snapshot)) {
32
+ delete pending[hash];
33
+ }
34
+ if (Object.keys(snapshot).length === 0)
35
+ return;
36
+ try {
37
+ await mkdir(dirname(statsPath), { recursive: true });
38
+ const stats = await readStats();
39
+ for (const [hash, count] of Object.entries(snapshot)) {
40
+ stats[hash] = (stats[hash] ?? 0) + count;
41
+ }
42
+ const tempPath = `${statsPath}.${randomUUID()}.tmp`;
43
+ try {
44
+ await writeFile(tempPath, JSON.stringify(stats), 'utf8');
45
+ await rename(tempPath, statsPath);
46
+ }
47
+ catch (error) {
48
+ const { unlink } = await import('node:fs/promises');
49
+ await unlink(tempPath).catch(() => { });
50
+ throw error;
51
+ }
52
+ }
53
+ catch (error) {
54
+ for (const [hash, count] of Object.entries(snapshot)) {
55
+ pending[hash] = (pending[hash] ?? 0) + count;
56
+ }
57
+ throw error;
58
+ }
59
+ }
60
+ function scheduleFlush() {
61
+ if (flushPromise)
62
+ return;
63
+ flushPromise = flushStats()
64
+ .catch((error) => {
65
+ console.error('Failed to persist usage stats:', error);
66
+ })
67
+ .finally(() => {
68
+ flushPromise = undefined;
69
+ });
70
+ }
71
+ const timer = setInterval(scheduleFlush, config.statsFlushIntervalMs);
72
+ timer.unref();
73
+ export async function closeStats() {
74
+ clearInterval(timer);
75
+ if (flushPromise) {
76
+ await flushPromise;
77
+ }
78
+ await flushStats();
79
+ }
80
+ //# sourceMappingURL=stats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.js","sourceRoot":"","sources":["../src/stats.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,52 @@
1
1
  {
2
- "name": "@vdegenne/kokoro-proxy",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
2
+ "name": "@vdegenne/kokoro-proxy",
3
+ "version": "0.1.2",
4
+ "description": "A lightweight Kokoro TTS proxy with audio caching and reusable utilities.",
5
+ "type": "module",
6
+ "exports": {
7
+ "./core": {
8
+ "types": "./lib/core.d.ts",
9
+ "import": "./lib/core.js"
10
+ }
11
+ },
12
+ "files": [
13
+ "lib"
14
+ ],
15
+ "bin": {
16
+ "kokoro-proxy": "./lib/server.js"
17
+ },
18
+ "scripts": {
19
+ "build": "wireit",
20
+ "serve": "wireit"
21
+ },
22
+ "devDependencies": {
23
+ "@types/node": "^26.6.5",
24
+ "typescript": "^7.0.2",
25
+ "wireit": "^0.15.0"
26
+ },
27
+ "wireit": {
28
+ "build": {
29
+ "command": "tsc --pretty",
30
+ "clean": "if-file-deleted",
31
+ "files": [
32
+ "./tsconfig.json",
33
+ "./src"
34
+ ],
35
+ "output": [
36
+ "./lib"
37
+ ]
38
+ },
39
+ "serve": {
40
+ "service": true,
41
+ "command": "node ./lib/server.js",
42
+ "dependencies": [
43
+ "build"
44
+ ],
45
+ "files": []
46
+ }
47
+ },
48
+ "dependencies": {
49
+ "@hono/node-server": "^2.1.5",
50
+ "hono": "^4.13.14"
51
+ }
52
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.