@wovin/tranz-cli 0.0.26
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/LICENSE +15 -0
- package/README.md +447 -0
- package/bin/dev.cmd +3 -0
- package/bin/dev.js +10 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +6 -0
- package/dist/commands/prep/index.d.ts +59 -0
- package/dist/commands/prep/index.js +177 -0
- package/dist/commands/scribe/index.d.ts +43 -0
- package/dist/commands/scribe/index.js +383 -0
- package/dist/commands/split/index.d.ts +23 -0
- package/dist/commands/split/index.js +95 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/utils/file-utils.d.ts +5 -0
- package/dist/utils/file-utils.js +15 -0
- package/dist/utils/formatBytes.d.ts +14 -0
- package/dist/utils/formatBytes.js +22 -0
- package/dist/utils/secrets.d.ts +18 -0
- package/dist/utils/secrets.js +40 -0
- package/oclif.manifest.json +284 -0
- package/package.json +90 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
// import { Logger } from '@ztax/logger/src'
|
|
3
|
+
import { getName } from "../../utils/file-utils.js";
|
|
4
|
+
const splitOnSil = (silenceInfo, output, sourceFileName) => {
|
|
5
|
+
const splitSeeks = [];
|
|
6
|
+
let report = `
|
|
7
|
+
Exclusions and inclusions:
|
|
8
|
+
`;
|
|
9
|
+
let segmentDuration = 0;
|
|
10
|
+
for (const [inx, eachSil] of silenceInfo.entries()) {
|
|
11
|
+
const { stSec, endSec, duration } = eachSil;
|
|
12
|
+
segmentDuration = duration;
|
|
13
|
+
const nextSil = silenceInfo[inx + 1];
|
|
14
|
+
if (!nextSil)
|
|
15
|
+
break;
|
|
16
|
+
const seekExclude = (duration - (Split.DEFAULTS.SILBUF * 2)).toFixed(3);
|
|
17
|
+
const segmentSt = +(endSec - Split.DEFAULTS.SILBUF).toFixed(3);
|
|
18
|
+
const segmentDur = +((nextSil?.stSec ?? 0) + Split.DEFAULTS.SILBUF - segmentSt).toFixed(3);
|
|
19
|
+
report += ` ex:${seekExclude} - in:${segmentDur}`;
|
|
20
|
+
const ts = 'todo';
|
|
21
|
+
const outputPath = `${output}/${inx}-${ts}-from-${sourceFileName}.wav`;
|
|
22
|
+
splitSeeks.push([segmentSt, segmentDur, ts, outputPath]);
|
|
23
|
+
}
|
|
24
|
+
report += ` ex:${segmentDuration.toFixed(3)} `;
|
|
25
|
+
};
|
|
26
|
+
const doSplit = () => {
|
|
27
|
+
const { WARN, LOG, DEBUG, VERBOSE, ERROR } = { WARN: console.warn, LOG: console.log, DEBUG: console.log, VERBOSE: console.debug, ERROR: (...Any) => new Error(JSON.stringify(Any)) }; // Logger.setup(Logger.INFO); // eslint-disable-line no-unused-vars
|
|
28
|
+
LOG('//TODO');
|
|
29
|
+
// return new Promise<SplitResult>(async (resolveFx, rejectFx) => {
|
|
30
|
+
// const transResults = await getWhisperTranscript(phase1OutputPath, output)
|
|
31
|
+
// if (!transResults?.trans?.transcription) return rejectFx(ERROR('bum transcription', transResults))
|
|
32
|
+
// const { code, trace, trans } = transResults
|
|
33
|
+
// const transTrace = ['Transcription results: ', { code, trace, trans }]
|
|
34
|
+
// info.traces.push(transTrace); DEBUG(transTrace)
|
|
35
|
+
// const transcription = trans.transcription as TranscriptionArray
|
|
36
|
+
// const isCreateInterimArtifacts = true // TODO implement chaining if this is false and add param
|
|
37
|
+
// const splitSeeks = []
|
|
38
|
+
// for (const [inx, eachTrans] of transcription.entries()) {
|
|
39
|
+
// const { offsets: { from: segSt, to: segEnd },
|
|
40
|
+
// timestamps: { from: tsFrom, to: tsTo },
|
|
41
|
+
// text, speaker_turn_next: isNewSpeaker } = eachTrans
|
|
42
|
+
// const {
|
|
43
|
+
// offsets: { from: nxSegSt, to: nxSegEnd },
|
|
44
|
+
// // timestamps: { from: tsFromNx, to: tsToNx },
|
|
45
|
+
// text: textNx, speaker_turn_next: isNewSpeakerNx } = transcription[inx + 1] ?? { offsets: {} }
|
|
46
|
+
// const gap = (nxSegSt ?? segEnd) - segEnd
|
|
47
|
+
// const duration = (segEnd + gap * 0.5) - segSt
|
|
48
|
+
// const ts = tsFrom // TODO absolute uts ts if startTimestamp is given or
|
|
49
|
+
// const outputPath = `${output}/${sourceFileName}-${inx}-${ts}-${text.substring(0, 42)}.wav` as string
|
|
50
|
+
// splitSeeks.push([segSt, duration, ts, outputPath])
|
|
51
|
+
// }
|
|
52
|
+
// console.log('starting segment splitting' /*, { splitSeeks, leadingSil, leadingSilEnd, inputParams }*/);
|
|
53
|
+
// for (const [inx, [sSt, sDur, ts, outputPath]] of splitSeeks.entries()) {
|
|
54
|
+
// console.log({ inx, sSt, sDur, ts })
|
|
55
|
+
// ffmpeg(phase2OutputPath, { logger: console }) //.inputOptions(inputParams)
|
|
56
|
+
// .seekInput(sSt)
|
|
57
|
+
// .duration(sDur)
|
|
58
|
+
// .save(outputPath as string)
|
|
59
|
+
// .on('end', function (stdout, stderr) {
|
|
60
|
+
// })
|
|
61
|
+
// }
|
|
62
|
+
// console.log('wrote', splitSeeks.length, 'files to', output)
|
|
63
|
+
// })
|
|
64
|
+
};
|
|
65
|
+
class Split extends Command {
|
|
66
|
+
async run() {
|
|
67
|
+
const { args: { input }, flags, flags: { output, silthr, sildur } } = await this.parse(Split);
|
|
68
|
+
const sourceFileName = getName(input);
|
|
69
|
+
this.log(`\n Preparing ${flags.input} into ${output}, maybe \n`);
|
|
70
|
+
const report = await Split.split();
|
|
71
|
+
console.log(report);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
Split.description = 'Split audio file - on silence, or split info from transcription';
|
|
75
|
+
Split.DEFAULTS = {
|
|
76
|
+
OUTDIR: './out',
|
|
77
|
+
SILDUR: '1.3',
|
|
78
|
+
SILBUF: 0.2,
|
|
79
|
+
SILTHR: '-35dB',
|
|
80
|
+
};
|
|
81
|
+
Split.flags = {
|
|
82
|
+
output: Flags.string({ char: 'o', description: 'output directory', required: false, default: Split.DEFAULTS.OUTDIR }),
|
|
83
|
+
silthr: Flags.string({ char: 's', description: 'silence threshold', required: false, default: Split.DEFAULTS.SILTHR }),
|
|
84
|
+
sildur: Flags.string({ char: 'd', description: 'silence duration', required: false, default: Split.DEFAULTS.SILDUR }),
|
|
85
|
+
};
|
|
86
|
+
Split.args = [
|
|
87
|
+
{ name: 'input', description: 'input file', required: true },
|
|
88
|
+
];
|
|
89
|
+
Split.split = doSplit;
|
|
90
|
+
Split.examples = [
|
|
91
|
+
`$ tranz prep split /pathTo/audio.wav
|
|
92
|
+
...TODO
|
|
93
|
+
`,
|
|
94
|
+
];
|
|
95
|
+
export default Split;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
export declare const getExt: (filePath: string) => string;
|
|
3
|
+
export declare const getName: (filePath: string) => string;
|
|
4
|
+
export declare const getNameWithExt: (filePath: string) => string;
|
|
5
|
+
export declare const getFileInfo: (filePath: string) => path.ParsedPath;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
export const getExt = (filePath) => {
|
|
3
|
+
return getFileInfo(filePath).ext;
|
|
4
|
+
};
|
|
5
|
+
export const getName = (filePath) => {
|
|
6
|
+
return getFileInfo(filePath).name;
|
|
7
|
+
};
|
|
8
|
+
export const getNameWithExt = (filePath) => {
|
|
9
|
+
const { name, ext } = getFileInfo(filePath);
|
|
10
|
+
return `${name}.${ext}`;
|
|
11
|
+
};
|
|
12
|
+
export const getFileInfo = (filePath) => {
|
|
13
|
+
const normed = path.normalize(filePath);
|
|
14
|
+
return path.parse(normed);
|
|
15
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format bytes into human-readable size string
|
|
3
|
+
*
|
|
4
|
+
* @param bytes - Number of bytes
|
|
5
|
+
* @param decimals - Number of decimal places (default: 1)
|
|
6
|
+
* @param binary - Use binary (1024) vs decimal (1000) units (default: true)
|
|
7
|
+
* @returns Formatted string like "36.9 MiB" or "38.7 MB"
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* formatBytes(38746650) // "36.9 MiB"
|
|
11
|
+
* formatBytes(38746650, 2) // "36.95 MiB"
|
|
12
|
+
* formatBytes(38746650, 1, false) // "38.7 MB"
|
|
13
|
+
*/
|
|
14
|
+
export declare function formatBytes(bytes: number, decimals?: number, binary?: boolean): string;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format bytes into human-readable size string
|
|
3
|
+
*
|
|
4
|
+
* @param bytes - Number of bytes
|
|
5
|
+
* @param decimals - Number of decimal places (default: 1)
|
|
6
|
+
* @param binary - Use binary (1024) vs decimal (1000) units (default: true)
|
|
7
|
+
* @returns Formatted string like "36.9 MiB" or "38.7 MB"
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* formatBytes(38746650) // "36.9 MiB"
|
|
11
|
+
* formatBytes(38746650, 2) // "36.95 MiB"
|
|
12
|
+
* formatBytes(38746650, 1, false) // "38.7 MB"
|
|
13
|
+
*/
|
|
14
|
+
export function formatBytes(bytes, decimals = 1, binary = true) {
|
|
15
|
+
if (bytes === 0)
|
|
16
|
+
return '0 B';
|
|
17
|
+
const k = binary ? 1024 : 1000;
|
|
18
|
+
const sizes = binary ? ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB'] : ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
|
|
19
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
20
|
+
const value = bytes / Math.pow(k, i);
|
|
21
|
+
return `${value.toFixed(decimals)} ${sizes[i]}`;
|
|
22
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load an API key from environment variable or secret file
|
|
3
|
+
* @param envVar - Environment variable name (e.g., 'MISTRAL_API_KEY')
|
|
4
|
+
* @param secretFile - Path to secret file (e.g., 'secret/mistral')
|
|
5
|
+
* @returns The API key, or undefined if not found
|
|
6
|
+
*/
|
|
7
|
+
export declare function loadSecret(envVar: string, secretFile: string): Promise<string | undefined>;
|
|
8
|
+
/**
|
|
9
|
+
* Load an API key from environment variable or secret file, with error handling
|
|
10
|
+
* @param envVar - Environment variable name
|
|
11
|
+
* @param secretFile - Path to secret file
|
|
12
|
+
* @param displayName - Human-readable name for error messages (e.g., 'Mistral API key')
|
|
13
|
+
* @returns The API key
|
|
14
|
+
* @throws If the key is not found
|
|
15
|
+
*/
|
|
16
|
+
export declare function loadSecretOrThrow(envVar: string, secretFile: string, displayName: string): Promise<string>;
|
|
17
|
+
export declare const loadApiKey: typeof loadSecret;
|
|
18
|
+
export declare const loadApiKeyOrThrow: typeof loadSecretOrThrow;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
/**
|
|
3
|
+
* Load an API key from environment variable or secret file
|
|
4
|
+
* @param envVar - Environment variable name (e.g., 'MISTRAL_API_KEY')
|
|
5
|
+
* @param secretFile - Path to secret file (e.g., 'secret/mistral')
|
|
6
|
+
* @returns The API key, or undefined if not found
|
|
7
|
+
*/
|
|
8
|
+
export async function loadSecret(envVar, secretFile) {
|
|
9
|
+
// Try environment variable first
|
|
10
|
+
const envValue = process.env[envVar];
|
|
11
|
+
if (envValue)
|
|
12
|
+
return envValue;
|
|
13
|
+
// Fall back to secret file
|
|
14
|
+
try {
|
|
15
|
+
const fileContent = fs.readFileSync(secretFile, 'utf-8');
|
|
16
|
+
return fileContent.trim() || undefined;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// File doesn't exist or can't be read
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Load an API key from environment variable or secret file, with error handling
|
|
25
|
+
* @param envVar - Environment variable name
|
|
26
|
+
* @param secretFile - Path to secret file
|
|
27
|
+
* @param displayName - Human-readable name for error messages (e.g., 'Mistral API key')
|
|
28
|
+
* @returns The API key
|
|
29
|
+
* @throws If the key is not found
|
|
30
|
+
*/
|
|
31
|
+
export async function loadSecretOrThrow(envVar, secretFile, displayName) {
|
|
32
|
+
const secret = await loadSecret(envVar, secretFile);
|
|
33
|
+
if (!secret) {
|
|
34
|
+
throw new Error(`${displayName} not found. Set ${envVar} environment variable or create ${secretFile} file`);
|
|
35
|
+
}
|
|
36
|
+
return secret;
|
|
37
|
+
}
|
|
38
|
+
// Keep backwards compatibility aliases
|
|
39
|
+
export const loadApiKey = loadSecret;
|
|
40
|
+
export const loadApiKeyOrThrow = loadSecretOrThrow;
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.0.26",
|
|
3
|
+
"commands": {
|
|
4
|
+
"prep": {
|
|
5
|
+
"id": "prep",
|
|
6
|
+
"description": "Prepare audio file - normalize, noise reduce, split on silence",
|
|
7
|
+
"strict": true,
|
|
8
|
+
"pluginName": "@wovin/tranz-cli",
|
|
9
|
+
"pluginAlias": "@wovin/tranz-cli",
|
|
10
|
+
"pluginType": "core",
|
|
11
|
+
"aliases": [],
|
|
12
|
+
"hiddenAliases": [],
|
|
13
|
+
"examples": [
|
|
14
|
+
"$ tranz prep AI_Could_Be_The_End_Of_Democracy.m4a' -s -35dB -d 1.5\n--> sets silence threshold to -35dB and min silence duration to 1.5s\n--> runs normalization and noise removal\n"
|
|
15
|
+
],
|
|
16
|
+
"flags": {
|
|
17
|
+
"norm": {
|
|
18
|
+
"name": "norm",
|
|
19
|
+
"type": "boolean",
|
|
20
|
+
"char": "n",
|
|
21
|
+
"description": "do normalization?",
|
|
22
|
+
"required": false,
|
|
23
|
+
"allowNo": false
|
|
24
|
+
},
|
|
25
|
+
"verbose": {
|
|
26
|
+
"name": "verbose",
|
|
27
|
+
"type": "boolean",
|
|
28
|
+
"char": "v",
|
|
29
|
+
"description": "trace more?",
|
|
30
|
+
"required": false,
|
|
31
|
+
"allowNo": false
|
|
32
|
+
},
|
|
33
|
+
"outdir": {
|
|
34
|
+
"name": "outdir",
|
|
35
|
+
"type": "option",
|
|
36
|
+
"char": "o",
|
|
37
|
+
"description": "output directory",
|
|
38
|
+
"required": false,
|
|
39
|
+
"multiple": false,
|
|
40
|
+
"default": "./out"
|
|
41
|
+
},
|
|
42
|
+
"silthr": {
|
|
43
|
+
"name": "silthr",
|
|
44
|
+
"type": "option",
|
|
45
|
+
"char": "s",
|
|
46
|
+
"description": "silence threshold",
|
|
47
|
+
"required": false,
|
|
48
|
+
"multiple": false,
|
|
49
|
+
"default": "-35dB"
|
|
50
|
+
},
|
|
51
|
+
"sildur": {
|
|
52
|
+
"name": "sildur",
|
|
53
|
+
"type": "option",
|
|
54
|
+
"char": "d",
|
|
55
|
+
"description": "silence duration",
|
|
56
|
+
"required": false,
|
|
57
|
+
"multiple": false,
|
|
58
|
+
"default": "1.3"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"args": {
|
|
62
|
+
"input": {
|
|
63
|
+
"name": "input",
|
|
64
|
+
"description": "input file",
|
|
65
|
+
"required": true
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"DEFAULTS": {
|
|
69
|
+
"SILDUR": "1.3",
|
|
70
|
+
"SILBUF": 0.2,
|
|
71
|
+
"SILTHR": "-35dB",
|
|
72
|
+
"RNNN": "https://raw.githubusercontent.com/GregorR/rnnoise-models/master/somnolent-hogwash-2018-09-01/sh.rnnn"
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"scribe": {
|
|
76
|
+
"id": "scribe",
|
|
77
|
+
"description": "Transcribe audio file - optionally prepare first",
|
|
78
|
+
"strict": true,
|
|
79
|
+
"pluginName": "@wovin/tranz-cli",
|
|
80
|
+
"pluginAlias": "@wovin/tranz-cli",
|
|
81
|
+
"pluginType": "core",
|
|
82
|
+
"aliases": [],
|
|
83
|
+
"hiddenAliases": [],
|
|
84
|
+
"examples": [
|
|
85
|
+
"$ tranz scribe 'path/to/16khz-audiofile.wav'\nruns whisper and outputs and saves a transcription json\n\n$ tranz scribe 'path/to/whatever-audiofile.mp3' -p\nfirst prepares and then runs whisper and outputs and saves a transcription json\n\n$ tranz scribe 'path/to/audiofile.mp3' -r mistral -m voxtral-mini-latest\ntranscribe using Mistral provider with specified model\n\n$ tranz scribe 'path/to/audiofile.mp3' -r mistral --timestamps segment\ntranscribe using Mistral with segment-level timestamps\n\n$ tranz scribe 'path/to/audiofile.mp3' -r mistral --diarization --timestamps word\ntranscribe using Mistral with speaker diarization and word-level timestamps\n\n$ tranz scribe 'path/to/audiofile.mp3' -r greenpt -l en\ntranscribe using GreenPT provider with language specification\n\n$ tranz scribe 'path/to/long-audio.mp3' --auto-split --max-segment 300\nautomatically split long audio at silence boundaries (max 5 min segments)\n"
|
|
86
|
+
],
|
|
87
|
+
"flags": {
|
|
88
|
+
"prep": {
|
|
89
|
+
"name": "prep",
|
|
90
|
+
"type": "boolean",
|
|
91
|
+
"char": "p",
|
|
92
|
+
"description": "do prep?",
|
|
93
|
+
"required": false,
|
|
94
|
+
"allowNo": false
|
|
95
|
+
},
|
|
96
|
+
"withGPU": {
|
|
97
|
+
"name": "withGPU",
|
|
98
|
+
"type": "boolean",
|
|
99
|
+
"char": "g",
|
|
100
|
+
"description": "use gpu?",
|
|
101
|
+
"required": false,
|
|
102
|
+
"allowNo": false
|
|
103
|
+
},
|
|
104
|
+
"norm": {
|
|
105
|
+
"name": "norm",
|
|
106
|
+
"type": "boolean",
|
|
107
|
+
"char": "n",
|
|
108
|
+
"description": "do normalization?",
|
|
109
|
+
"required": false,
|
|
110
|
+
"allowNo": false
|
|
111
|
+
},
|
|
112
|
+
"output": {
|
|
113
|
+
"name": "output",
|
|
114
|
+
"type": "option",
|
|
115
|
+
"char": "o",
|
|
116
|
+
"description": "output directory",
|
|
117
|
+
"required": false,
|
|
118
|
+
"multiple": false,
|
|
119
|
+
"default": "./out"
|
|
120
|
+
},
|
|
121
|
+
"diarization": {
|
|
122
|
+
"name": "diarization",
|
|
123
|
+
"type": "boolean",
|
|
124
|
+
"char": "d",
|
|
125
|
+
"description": "enable speaker diarization",
|
|
126
|
+
"required": false,
|
|
127
|
+
"allowNo": false
|
|
128
|
+
},
|
|
129
|
+
"separate_speakers": {
|
|
130
|
+
"name": "separate_speakers",
|
|
131
|
+
"type": "boolean",
|
|
132
|
+
"description": "separate via diarization (deprecated: use --diarization)",
|
|
133
|
+
"required": false,
|
|
134
|
+
"allowNo": false,
|
|
135
|
+
"deprecated": {
|
|
136
|
+
"message": "use --diarization instead"
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
"provider": {
|
|
140
|
+
"name": "provider",
|
|
141
|
+
"type": "option",
|
|
142
|
+
"char": "r",
|
|
143
|
+
"description": "transcription provider (whisper, mistral, greenpt)",
|
|
144
|
+
"multiple": false,
|
|
145
|
+
"options": [
|
|
146
|
+
"whisper",
|
|
147
|
+
"mistral",
|
|
148
|
+
"voxtral",
|
|
149
|
+
"greenpt"
|
|
150
|
+
],
|
|
151
|
+
"default": "whisper"
|
|
152
|
+
},
|
|
153
|
+
"model": {
|
|
154
|
+
"name": "model",
|
|
155
|
+
"type": "option",
|
|
156
|
+
"char": "m",
|
|
157
|
+
"description": "model name (provider-specific)",
|
|
158
|
+
"multiple": false
|
|
159
|
+
},
|
|
160
|
+
"language": {
|
|
161
|
+
"name": "language",
|
|
162
|
+
"type": "option",
|
|
163
|
+
"char": "l",
|
|
164
|
+
"description": "language code (e.g., en, de, fr)",
|
|
165
|
+
"multiple": false
|
|
166
|
+
},
|
|
167
|
+
"timestamps": {
|
|
168
|
+
"name": "timestamps",
|
|
169
|
+
"type": "option",
|
|
170
|
+
"description": "enable timestamp granularity (segment, word) - Mistral only",
|
|
171
|
+
"multiple": false,
|
|
172
|
+
"options": [
|
|
173
|
+
"segment",
|
|
174
|
+
"word"
|
|
175
|
+
]
|
|
176
|
+
},
|
|
177
|
+
"no-pauses": {
|
|
178
|
+
"name": "no-pauses",
|
|
179
|
+
"type": "boolean",
|
|
180
|
+
"description": "disable pause-based line break formatting in transcript preview",
|
|
181
|
+
"allowNo": false
|
|
182
|
+
},
|
|
183
|
+
"auto-split": {
|
|
184
|
+
"name": "auto-split",
|
|
185
|
+
"type": "boolean",
|
|
186
|
+
"description": "automatically split long audio files at silence boundaries",
|
|
187
|
+
"allowNo": false
|
|
188
|
+
},
|
|
189
|
+
"max-segment": {
|
|
190
|
+
"name": "max-segment",
|
|
191
|
+
"type": "option",
|
|
192
|
+
"description": "maximum segment duration in seconds for auto-split (default: 600)",
|
|
193
|
+
"multiple": false,
|
|
194
|
+
"default": 600
|
|
195
|
+
},
|
|
196
|
+
"split-silence-dur": {
|
|
197
|
+
"name": "split-silence-dur",
|
|
198
|
+
"type": "option",
|
|
199
|
+
"description": "minimum silence duration for split points (default: 1.0)",
|
|
200
|
+
"multiple": false,
|
|
201
|
+
"default": "1.0"
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
"args": {
|
|
205
|
+
"input": {
|
|
206
|
+
"name": "input",
|
|
207
|
+
"description": "input file",
|
|
208
|
+
"required": true
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
"DEFAULTS": {
|
|
212
|
+
"DIARIZE": false,
|
|
213
|
+
"SILDUR": "1.3",
|
|
214
|
+
"SILBUF": 0.2,
|
|
215
|
+
"SILTHR": "-35dB",
|
|
216
|
+
"MODEL_KEYS": {
|
|
217
|
+
"tinyd": "ggml-small.en-tdrz.bin",
|
|
218
|
+
"small": "ggml-small.bin",
|
|
219
|
+
"medium": "ggml-medium.bin"
|
|
220
|
+
},
|
|
221
|
+
"MODELS": {
|
|
222
|
+
"tinyd": "https://huggingface.co/akashmjn/tinydiarize-whisper.cpp/resolve/main/ggml-small.en-tdrz.bin",
|
|
223
|
+
"small": "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.bin",
|
|
224
|
+
"medium": "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-medium.bin"
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
"split": {
|
|
229
|
+
"id": "split",
|
|
230
|
+
"description": "Split audio file - on silence, or split info from transcription",
|
|
231
|
+
"strict": true,
|
|
232
|
+
"pluginName": "@wovin/tranz-cli",
|
|
233
|
+
"pluginAlias": "@wovin/tranz-cli",
|
|
234
|
+
"pluginType": "core",
|
|
235
|
+
"aliases": [],
|
|
236
|
+
"hiddenAliases": [],
|
|
237
|
+
"examples": [
|
|
238
|
+
"$ tranz prep split /pathTo/audio.wav\n...TODO\n"
|
|
239
|
+
],
|
|
240
|
+
"flags": {
|
|
241
|
+
"output": {
|
|
242
|
+
"name": "output",
|
|
243
|
+
"type": "option",
|
|
244
|
+
"char": "o",
|
|
245
|
+
"description": "output directory",
|
|
246
|
+
"required": false,
|
|
247
|
+
"multiple": false,
|
|
248
|
+
"default": "./out"
|
|
249
|
+
},
|
|
250
|
+
"silthr": {
|
|
251
|
+
"name": "silthr",
|
|
252
|
+
"type": "option",
|
|
253
|
+
"char": "s",
|
|
254
|
+
"description": "silence threshold",
|
|
255
|
+
"required": false,
|
|
256
|
+
"multiple": false,
|
|
257
|
+
"default": "-35dB"
|
|
258
|
+
},
|
|
259
|
+
"sildur": {
|
|
260
|
+
"name": "sildur",
|
|
261
|
+
"type": "option",
|
|
262
|
+
"char": "d",
|
|
263
|
+
"description": "silence duration",
|
|
264
|
+
"required": false,
|
|
265
|
+
"multiple": false,
|
|
266
|
+
"default": "1.3"
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
"args": {
|
|
270
|
+
"input": {
|
|
271
|
+
"name": "input",
|
|
272
|
+
"description": "input file",
|
|
273
|
+
"required": true
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
"DEFAULTS": {
|
|
277
|
+
"OUTDIR": "./out",
|
|
278
|
+
"SILDUR": "1.3",
|
|
279
|
+
"SILBUF": 0.2,
|
|
280
|
+
"SILTHR": "-35dB"
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "module",
|
|
3
|
+
"name": "@wovin/tranz-cli",
|
|
4
|
+
"version": "0.0.26",
|
|
5
|
+
"description": "CLI for audio and video transcription",
|
|
6
|
+
"author": "gotjoshua @gotjoshua",
|
|
7
|
+
"bin": {
|
|
8
|
+
"tranz": "./bin/run.js",
|
|
9
|
+
"tranzz": "./bin/dev.js"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://gitlab.com/onezoomin/ztax/tranz",
|
|
12
|
+
"license": "ISC",
|
|
13
|
+
"main": "",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://gitlab.com/wovin/wovin.git",
|
|
17
|
+
"directory": "packages/@wovin/tranz-cli"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"/bin",
|
|
21
|
+
"/dist",
|
|
22
|
+
"/npm-shrinkwrap.json",
|
|
23
|
+
"/oclif.manifest.json"
|
|
24
|
+
],
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@dharmendrasha/ffmpeg-normalize": "^1.8.2",
|
|
30
|
+
"@oclif/core": "^2",
|
|
31
|
+
"@oclif/plugin-help": "^5",
|
|
32
|
+
"@oclif/plugin-plugins": "^3.1.8",
|
|
33
|
+
"@ztax/logger": "^0.2.1",
|
|
34
|
+
"fluent-ffmpeg": "^2.1.2",
|
|
35
|
+
"@wovin/tranz": "^0.0.26"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@oclif/test": "^2.4.2",
|
|
39
|
+
"@types/chai": "^5.2.3",
|
|
40
|
+
"@types/fluent-ffmpeg": "^2.1.21",
|
|
41
|
+
"@types/mocha": "^10.0.10",
|
|
42
|
+
"@types/node": "^24.10.1",
|
|
43
|
+
"chai": "^4",
|
|
44
|
+
"eslint": "^7.32.0",
|
|
45
|
+
"eslint-config-oclif": "^4",
|
|
46
|
+
"eslint-config-oclif-typescript": "^1.0.3",
|
|
47
|
+
"mocha": "^9",
|
|
48
|
+
"oclif": "^3.11.3",
|
|
49
|
+
"shx": "^0.4.0",
|
|
50
|
+
"ts-node": "^10.9.1",
|
|
51
|
+
"tslib": "^2.6.1",
|
|
52
|
+
"typescript": "^5.9.3",
|
|
53
|
+
"whisper-node": "^1.1.1"
|
|
54
|
+
},
|
|
55
|
+
"oclif": {
|
|
56
|
+
"bin": "tranz",
|
|
57
|
+
"dirname": "tranz",
|
|
58
|
+
"commands": "./dist/commands",
|
|
59
|
+
"plugins": [
|
|
60
|
+
"@oclif/plugin-help",
|
|
61
|
+
"@oclif/plugin-plugins"
|
|
62
|
+
],
|
|
63
|
+
"topicSeparator": " ",
|
|
64
|
+
"topics": {
|
|
65
|
+
"prep": {
|
|
66
|
+
"description": "Prepare audio for transcription"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"engines": {
|
|
71
|
+
"node": ">=12.0.0"
|
|
72
|
+
},
|
|
73
|
+
"bugs": "https://gitlab.com/onezoomin/ztax/tranz/-/issues",
|
|
74
|
+
"keywords": [
|
|
75
|
+
"oclif",
|
|
76
|
+
"cli",
|
|
77
|
+
"transcription"
|
|
78
|
+
],
|
|
79
|
+
"types": "dist/index.d.ts",
|
|
80
|
+
"scripts": {
|
|
81
|
+
"tranz": "./bin/dev.js",
|
|
82
|
+
"oclif": "oclif",
|
|
83
|
+
"manifest": "ts-node oclif manifest",
|
|
84
|
+
"build": "shx rm -rf dist && tsc -b",
|
|
85
|
+
"lint": "eslint . --ext .ts --config .eslintrc",
|
|
86
|
+
"posttest": "npm run lint",
|
|
87
|
+
"test": "mocha --forbid-only \"test/**/*.test.ts\"",
|
|
88
|
+
"version": "oclif readme && git add README.md"
|
|
89
|
+
}
|
|
90
|
+
}
|