termux-stt 2.0.4 → 2.1.0

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/hybrid.js CHANGED
@@ -1,64 +1,68 @@
1
- /**
2
- * HybridEngine for Node.js - Vosk X-Vector Diarization + Whisper STT
3
- */
4
-
5
- const { spawn } = require('child_process');
6
- const { Engine, TranscriptResult, Segment } = require('./engine');
7
-
8
- class HybridEngine extends Engine {
9
- constructor(config = {}) {
10
- super(config);
11
- this.model = config.model || 'base';
12
- this.lang = config.lang || 'ko';
13
- this.numSpeakers = config.numSpeakers || 2;
14
- }
15
-
16
- async transcribe(audioPath, options = {}) {
17
- return this.diarize(audioPath, this.numSpeakers);
18
- }
19
-
20
- async diarize(audioPath, numSpeakers = 2) {
21
- return new Promise((resolve, reject) => {
22
- const args = [
23
- '-m', 'termux_stt.cli.main',
24
- 'diarize',
25
- '--speakers', String(numSpeakers),
26
- '--format', 'json',
27
- audioPath
28
- ];
29
-
30
- const proc = spawn('python', args, { env: process.env });
31
- let stdout = '';
32
- let stderr = '';
33
-
34
- proc.stdout.on('data', (d) => { stdout += d.toString(); });
35
- proc.stderr.on('data', (d) => { stderr += d.toString(); });
36
-
37
- proc.on('close', (code) => {
38
- if (code !== 0) {
39
- return reject(new Error(`Hybrid diarization failed (code ${code}): ${stderr}`));
40
- }
41
- try {
42
- const parsed = JSON.parse(stdout);
43
- const segments = (parsed.segments || []).map(
44
- s => new Segment(s.start, s.end, s.text, s.speaker, s.confidence)
45
- );
46
- resolve(new TranscriptResult(parsed.text, segments, parsed.language || this.lang, parsed.duration));
47
- } catch (e) {
48
- resolve(new TranscriptResult(stdout.trim(), [new Segment(0, 0, stdout.trim())], this.lang));
49
- }
50
- });
51
- });
52
- }
53
-
54
- getInfo() {
55
- return {
56
- name: 'Hybrid Neural Diarizer + Whisper (Node.js)',
57
- model: this.model,
58
- language: this.lang,
59
- numSpeakers: this.numSpeakers
60
- };
61
- }
62
- }
63
-
64
- module.exports = { HybridEngine };
1
+ /**
2
+ * Copyright (c) 2026 Uno Kim / AMEVA Foundation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0 OR Commercial-License
4
+ */
5
+ /**
6
+ * HybridEngine for Node.js - Vosk X-Vector Diarization + Whisper STT
7
+ */
8
+
9
+ const { spawn } = require('child_process');
10
+ const { Engine, TranscriptResult, Segment } = require('./engine');
11
+
12
+ class HybridEngine extends Engine {
13
+ constructor(config = {}) {
14
+ super(config);
15
+ this.model = config.model || 'base';
16
+ this.lang = config.lang || 'ko';
17
+ this.numSpeakers = config.numSpeakers || 2;
18
+ }
19
+
20
+ async transcribe(audioPath, options = {}) {
21
+ return this.diarize(audioPath, this.numSpeakers);
22
+ }
23
+
24
+ async diarize(audioPath, numSpeakers = 2) {
25
+ return new Promise((resolve, reject) => {
26
+ const args = [
27
+ '-m', 'termux_stt.cli.main',
28
+ 'diarize',
29
+ '--speakers', String(numSpeakers),
30
+ '--format', 'json',
31
+ audioPath
32
+ ];
33
+
34
+ const proc = spawn('python', args, { env: process.env });
35
+ let stdout = '';
36
+ let stderr = '';
37
+
38
+ proc.stdout.on('data', (d) => { stdout += d.toString(); });
39
+ proc.stderr.on('data', (d) => { stderr += d.toString(); });
40
+
41
+ proc.on('close', (code) => {
42
+ if (code !== 0) {
43
+ return reject(new Error(`Hybrid diarization failed (code ${code}): ${stderr}`));
44
+ }
45
+ try {
46
+ const parsed = JSON.parse(stdout);
47
+ const segments = (parsed.segments || []).map(
48
+ s => new Segment(s.start, s.end, s.text, s.speaker, s.confidence)
49
+ );
50
+ resolve(new TranscriptResult(parsed.text, segments, parsed.language || this.lang, parsed.duration));
51
+ } catch (e) {
52
+ resolve(new TranscriptResult(stdout.trim(), [new Segment(0, 0, stdout.trim())], this.lang));
53
+ }
54
+ });
55
+ });
56
+ }
57
+
58
+ getInfo() {
59
+ return {
60
+ name: 'Hybrid Neural Diarizer + Whisper (Node.js)',
61
+ model: this.model,
62
+ language: this.lang,
63
+ numSpeakers: this.numSpeakers
64
+ };
65
+ }
66
+ }
67
+
68
+ module.exports = { HybridEngine };
package/lib/sherpa.js CHANGED
@@ -1,97 +1,101 @@
1
- /**
2
- * SherpaEngine for Node.js - Non-Autoregressive SenseVoice STT & Neural Diarization
3
- */
4
-
5
- const { spawn } = require('child_process');
6
- const { Engine, TranscriptResult, Segment } = require('./engine');
7
-
8
- class SherpaEngine extends Engine {
9
- constructor(config = {}) {
10
- super(config);
11
- this.model = config.model || 'sensevoice-small-int8';
12
- this.lang = config.lang || 'ko';
13
- this.threads = config.threads || 4;
14
- }
15
-
16
- async transcribe(audioPath, options = {}) {
17
- return new Promise((resolve, reject) => {
18
- const args = [
19
- '-m', 'termux_stt.cli.main',
20
- 'transcribe',
21
- '--engine', 'sherpa',
22
- '--model', this.model,
23
- '--lang', this.lang,
24
- '--format', 'json',
25
- audioPath
26
- ];
27
-
28
- const proc = spawn('python', args, { env: process.env });
29
- let stdout = '';
30
- let stderr = '';
31
-
32
- proc.stdout.on('data', (d) => { stdout += d.toString(); });
33
- proc.stderr.on('data', (d) => { stderr += d.toString(); });
34
-
35
- proc.on('close', (code) => {
36
- if (code !== 0) {
37
- return reject(new Error(`Sherpa transcription failed (code ${code}): ${stderr}`));
38
- }
39
- try {
40
- const parsed = JSON.parse(stdout);
41
- const segments = (parsed.segments || []).map(
42
- s => new Segment(s.start, s.end, s.text, s.speaker, s.confidence)
43
- );
44
- resolve(new TranscriptResult(parsed.text, segments, parsed.language || this.lang, parsed.duration));
45
- } catch (e) {
46
- resolve(new TranscriptResult(stdout.trim(), [new Segment(0, 0, stdout.trim())], this.lang));
47
- }
48
- });
49
- });
50
- }
51
-
52
- async diarize(audioPath, numSpeakers = 2) {
53
- return new Promise((resolve, reject) => {
54
- const args = [
55
- '-m', 'termux_stt.cli.main',
56
- 'diarize',
57
- '--engine', 'sherpa',
58
- '--speakers', String(numSpeakers),
59
- '--format', 'json',
60
- audioPath
61
- ];
62
-
63
- const proc = spawn('python', args, { env: process.env });
64
- let stdout = '';
65
- let stderr = '';
66
-
67
- proc.stdout.on('data', (d) => { stdout += d.toString(); });
68
- proc.stderr.on('data', (d) => { stderr += d.toString(); });
69
-
70
- proc.on('close', (code) => {
71
- if (code !== 0) {
72
- return reject(new Error(`Sherpa diarization failed (code ${code}): ${stderr}`));
73
- }
74
- try {
75
- const parsed = JSON.parse(stdout);
76
- const segments = (parsed.segments || []).map(
77
- s => new Segment(s.start, s.end, s.text, s.speaker, s.confidence)
78
- );
79
- resolve(new TranscriptResult(parsed.text, segments, parsed.language || this.lang, parsed.duration));
80
- } catch (e) {
81
- resolve(new TranscriptResult(stdout.trim(), [new Segment(0, 0, stdout.trim())], this.lang));
82
- }
83
- });
84
- });
85
- }
86
-
87
- getInfo() {
88
- return {
89
- name: 'Sherpa-ONNX (Node.js)',
90
- model: this.model,
91
- language: this.lang,
92
- threads: this.threads
93
- };
94
- }
95
- }
96
-
97
- module.exports = { SherpaEngine };
1
+ /**
2
+ * Copyright (c) 2026 Uno Kim / AMEVA Foundation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0 OR Commercial-License
4
+ */
5
+ /**
6
+ * SherpaEngine for Node.js - Non-Autoregressive SenseVoice STT & Neural Diarization
7
+ */
8
+
9
+ const { spawn } = require('child_process');
10
+ const { Engine, TranscriptResult, Segment } = require('./engine');
11
+
12
+ class SherpaEngine extends Engine {
13
+ constructor(config = {}) {
14
+ super(config);
15
+ this.model = config.model || 'sensevoice-small-int8';
16
+ this.lang = config.lang || 'ko';
17
+ this.threads = config.threads || 4;
18
+ }
19
+
20
+ async transcribe(audioPath, options = {}) {
21
+ return new Promise((resolve, reject) => {
22
+ const args = [
23
+ '-m', 'termux_stt.cli.main',
24
+ 'transcribe',
25
+ '--engine', 'sherpa',
26
+ '--model', this.model,
27
+ '--lang', this.lang,
28
+ '--format', 'json',
29
+ audioPath
30
+ ];
31
+
32
+ const proc = spawn('python', args, { env: process.env });
33
+ let stdout = '';
34
+ let stderr = '';
35
+
36
+ proc.stdout.on('data', (d) => { stdout += d.toString(); });
37
+ proc.stderr.on('data', (d) => { stderr += d.toString(); });
38
+
39
+ proc.on('close', (code) => {
40
+ if (code !== 0) {
41
+ return reject(new Error(`Sherpa transcription failed (code ${code}): ${stderr}`));
42
+ }
43
+ try {
44
+ const parsed = JSON.parse(stdout);
45
+ const segments = (parsed.segments || []).map(
46
+ s => new Segment(s.start, s.end, s.text, s.speaker, s.confidence)
47
+ );
48
+ resolve(new TranscriptResult(parsed.text, segments, parsed.language || this.lang, parsed.duration));
49
+ } catch (e) {
50
+ resolve(new TranscriptResult(stdout.trim(), [new Segment(0, 0, stdout.trim())], this.lang));
51
+ }
52
+ });
53
+ });
54
+ }
55
+
56
+ async diarize(audioPath, numSpeakers = 2) {
57
+ return new Promise((resolve, reject) => {
58
+ const args = [
59
+ '-m', 'termux_stt.cli.main',
60
+ 'diarize',
61
+ '--engine', 'sherpa',
62
+ '--speakers', String(numSpeakers),
63
+ '--format', 'json',
64
+ audioPath
65
+ ];
66
+
67
+ const proc = spawn('python', args, { env: process.env });
68
+ let stdout = '';
69
+ let stderr = '';
70
+
71
+ proc.stdout.on('data', (d) => { stdout += d.toString(); });
72
+ proc.stderr.on('data', (d) => { stderr += d.toString(); });
73
+
74
+ proc.on('close', (code) => {
75
+ if (code !== 0) {
76
+ return reject(new Error(`Sherpa diarization failed (code ${code}): ${stderr}`));
77
+ }
78
+ try {
79
+ const parsed = JSON.parse(stdout);
80
+ const segments = (parsed.segments || []).map(
81
+ s => new Segment(s.start, s.end, s.text, s.speaker, s.confidence)
82
+ );
83
+ resolve(new TranscriptResult(parsed.text, segments, parsed.language || this.lang, parsed.duration));
84
+ } catch (e) {
85
+ resolve(new TranscriptResult(stdout.trim(), [new Segment(0, 0, stdout.trim())], this.lang));
86
+ }
87
+ });
88
+ });
89
+ }
90
+
91
+ getInfo() {
92
+ return {
93
+ name: 'Sherpa-ONNX (Node.js)',
94
+ model: this.model,
95
+ language: this.lang,
96
+ threads: this.threads
97
+ };
98
+ }
99
+ }
100
+
101
+ module.exports = { SherpaEngine };
package/lib/whisper.js CHANGED
@@ -1,79 +1,83 @@
1
- /**
2
- * WhisperEngine for Node.js - wraps whisper.cpp via spawn or termux-stt CLI
3
- */
4
-
5
- const { spawn } = require('child_process');
6
- const fs = require('fs');
7
- const path = require('path');
8
- const { Engine, TranscriptResult, Segment } = require('./engine');
9
-
10
- class WhisperEngine extends Engine {
11
- constructor(config = {}) {
12
- super(config);
13
- this.model = config.model || 'base';
14
- this.lang = config.lang || 'ko';
15
- this.threads = config.threads || null;
16
- this.device = config.device || 'auto';
17
- }
18
-
19
- async transcribe(audioPath, options = {}) {
20
- return new Promise((resolve, reject) => {
21
- const device = options.device || this.device || 'auto';
22
- const model = options.model || this.model || 'base';
23
- const lang = options.lang || this.lang || 'ko';
24
- const threads = options.threads || this.threads;
25
-
26
- const args = [
27
- '-m', 'termux_stt.cli.main',
28
- 'transcribe',
29
- '--engine', 'whisper',
30
- '--model', model,
31
- '--lang', lang,
32
- '--device', device,
33
- '--format', 'json',
34
- ];
35
-
36
- if (threads) {
37
- args.push('--threads', String(threads));
38
- }
39
-
40
- args.push(audioPath);
41
-
42
- const pythonExe = process.env.PYTHON || (process.platform === 'win32' ? 'python' : 'python3');
43
- const proc = spawn(pythonExe, args, { env: process.env });
44
- let stdout = '';
45
- let stderr = '';
46
-
47
- proc.stdout.on('data', (d) => { stdout += d.toString(); });
48
- proc.stderr.on('data', (d) => { stderr += d.toString(); });
49
-
50
- proc.on('close', (code) => {
51
- if (code !== 0) {
52
- return reject(new Error(`whisper transcription failed (code ${code}): ${stderr}`));
53
- }
54
- try {
55
- const parsed = JSON.parse(stdout);
56
- const segments = (parsed.segments || []).map(
57
- s => new Segment(s.start, s.end, s.text, s.speaker, s.confidence)
58
- );
59
- resolve(new TranscriptResult(parsed.text, segments, parsed.language || lang, parsed.duration));
60
- } catch (e) {
61
- // Fallback if stdout was plain text
62
- resolve(new TranscriptResult(stdout.trim(), [new Segment(0, 0, stdout.trim())], lang));
63
- }
64
- });
65
- });
66
- }
67
-
68
- getInfo() {
69
- return {
70
- name: 'whisper.cpp (Node.js)',
71
- model: this.model,
72
- language: this.lang,
73
- threads: this.threads,
74
- device: this.device,
75
- };
76
- }
77
- }
78
-
79
- module.exports = { WhisperEngine };
1
+ /**
2
+ * Copyright (c) 2026 Uno Kim / AMEVA Foundation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0 OR Commercial-License
4
+ */
5
+ /**
6
+ * WhisperEngine for Node.js - wraps whisper.cpp via spawn or termux-stt CLI
7
+ */
8
+
9
+ const { spawn } = require('child_process');
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+ const { Engine, TranscriptResult, Segment } = require('./engine');
13
+
14
+ class WhisperEngine extends Engine {
15
+ constructor(config = {}) {
16
+ super(config);
17
+ this.model = config.model || 'base';
18
+ this.lang = config.lang || 'ko';
19
+ this.threads = config.threads || null;
20
+ this.device = config.device || 'auto';
21
+ }
22
+
23
+ async transcribe(audioPath, options = {}) {
24
+ return new Promise((resolve, reject) => {
25
+ const device = options.device || this.device || 'auto';
26
+ const model = options.model || this.model || 'base';
27
+ const lang = options.lang || this.lang || 'ko';
28
+ const threads = options.threads || this.threads;
29
+
30
+ const args = [
31
+ '-m', 'termux_stt.cli.main',
32
+ 'transcribe',
33
+ '--engine', 'whisper',
34
+ '--model', model,
35
+ '--lang', lang,
36
+ '--device', device,
37
+ '--format', 'json',
38
+ ];
39
+
40
+ if (threads) {
41
+ args.push('--threads', String(threads));
42
+ }
43
+
44
+ args.push(audioPath);
45
+
46
+ const pythonExe = process.env.PYTHON || (process.platform === 'win32' ? 'python' : 'python3');
47
+ const proc = spawn(pythonExe, args, { env: process.env });
48
+ let stdout = '';
49
+ let stderr = '';
50
+
51
+ proc.stdout.on('data', (d) => { stdout += d.toString(); });
52
+ proc.stderr.on('data', (d) => { stderr += d.toString(); });
53
+
54
+ proc.on('close', (code) => {
55
+ if (code !== 0) {
56
+ return reject(new Error(`whisper transcription failed (code ${code}): ${stderr}`));
57
+ }
58
+ try {
59
+ const parsed = JSON.parse(stdout);
60
+ const segments = (parsed.segments || []).map(
61
+ s => new Segment(s.start, s.end, s.text, s.speaker, s.confidence)
62
+ );
63
+ resolve(new TranscriptResult(parsed.text, segments, parsed.language || lang, parsed.duration));
64
+ } catch (e) {
65
+ // Fallback if stdout was plain text
66
+ resolve(new TranscriptResult(stdout.trim(), [new Segment(0, 0, stdout.trim())], lang));
67
+ }
68
+ });
69
+ });
70
+ }
71
+
72
+ getInfo() {
73
+ return {
74
+ name: 'whisper.cpp (Node.js)',
75
+ model: this.model,
76
+ language: this.lang,
77
+ threads: this.threads,
78
+ device: this.device,
79
+ };
80
+ }
81
+ }
82
+
83
+ module.exports = { WhisperEngine };