@bryan-gc/transcribe-cli 1.0.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.
Files changed (114) hide show
  1. package/README.md +46 -0
  2. package/dist/audio/audioPlayer.d.ts +7 -0
  3. package/dist/audio/audioPlayer.d.ts.map +1 -0
  4. package/dist/audio/audioPlayer.js +49 -0
  5. package/dist/audio/audioPlayer.js.map +1 -0
  6. package/dist/audio/micDevices.d.ts +17 -0
  7. package/dist/audio/micDevices.d.ts.map +1 -0
  8. package/dist/audio/micDevices.js +120 -0
  9. package/dist/audio/micDevices.js.map +1 -0
  10. package/dist/audio/recorder.d.ts +16 -0
  11. package/dist/audio/recorder.d.ts.map +1 -0
  12. package/dist/audio/recorder.js +149 -0
  13. package/dist/audio/recorder.js.map +1 -0
  14. package/dist/components/App.d.ts +5 -0
  15. package/dist/components/App.d.ts.map +1 -0
  16. package/dist/components/App.js +279 -0
  17. package/dist/components/App.js.map +1 -0
  18. package/dist/components/MicTest.d.ts +10 -0
  19. package/dist/components/MicTest.d.ts.map +1 -0
  20. package/dist/components/MicTest.js +150 -0
  21. package/dist/components/MicTest.js.map +1 -0
  22. package/dist/components/Picker.d.ts +13 -0
  23. package/dist/components/Picker.d.ts.map +1 -0
  24. package/dist/components/Picker.js +20 -0
  25. package/dist/components/Picker.js.map +1 -0
  26. package/dist/components/SetupPrompt.d.ts +8 -0
  27. package/dist/components/SetupPrompt.d.ts.map +1 -0
  28. package/dist/components/SetupPrompt.js +39 -0
  29. package/dist/components/SetupPrompt.js.map +1 -0
  30. package/dist/config/configManager.d.ts +15 -0
  31. package/dist/config/configManager.d.ts.map +1 -0
  32. package/dist/config/configManager.js +50 -0
  33. package/dist/config/configManager.js.map +1 -0
  34. package/dist/constants.d.ts +134 -0
  35. package/dist/constants.d.ts.map +1 -0
  36. package/dist/constants.js +169 -0
  37. package/dist/constants.js.map +1 -0
  38. package/dist/index.d.ts +3 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +34 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/src/audio/audioPlayer.d.ts +7 -0
  43. package/dist/src/audio/audioPlayer.d.ts.map +1 -0
  44. package/dist/src/audio/audioPlayer.js +49 -0
  45. package/dist/src/audio/audioPlayer.js.map +1 -0
  46. package/dist/src/audio/micDevices.d.ts +17 -0
  47. package/dist/src/audio/micDevices.d.ts.map +1 -0
  48. package/dist/src/audio/micDevices.js +120 -0
  49. package/dist/src/audio/micDevices.js.map +1 -0
  50. package/dist/src/audio/recorder.d.ts +16 -0
  51. package/dist/src/audio/recorder.d.ts.map +1 -0
  52. package/dist/src/audio/recorder.js +149 -0
  53. package/dist/src/audio/recorder.js.map +1 -0
  54. package/dist/src/components/App.d.ts +5 -0
  55. package/dist/src/components/App.d.ts.map +1 -0
  56. package/dist/src/components/App.js +279 -0
  57. package/dist/src/components/App.js.map +1 -0
  58. package/dist/src/components/MicTest.d.ts +10 -0
  59. package/dist/src/components/MicTest.d.ts.map +1 -0
  60. package/dist/src/components/MicTest.js +150 -0
  61. package/dist/src/components/MicTest.js.map +1 -0
  62. package/dist/src/components/Picker.d.ts +13 -0
  63. package/dist/src/components/Picker.d.ts.map +1 -0
  64. package/dist/src/components/Picker.js +20 -0
  65. package/dist/src/components/Picker.js.map +1 -0
  66. package/dist/src/components/SetupPrompt.d.ts +8 -0
  67. package/dist/src/components/SetupPrompt.d.ts.map +1 -0
  68. package/dist/src/components/SetupPrompt.js +39 -0
  69. package/dist/src/components/SetupPrompt.js.map +1 -0
  70. package/dist/src/config/configManager.d.ts +15 -0
  71. package/dist/src/config/configManager.d.ts.map +1 -0
  72. package/dist/src/config/configManager.js +50 -0
  73. package/dist/src/config/configManager.js.map +1 -0
  74. package/dist/src/constants.d.ts +134 -0
  75. package/dist/src/constants.d.ts.map +1 -0
  76. package/dist/src/constants.js +169 -0
  77. package/dist/src/constants.js.map +1 -0
  78. package/dist/src/index.d.ts +3 -0
  79. package/dist/src/index.d.ts.map +1 -0
  80. package/dist/src/index.js +34 -0
  81. package/dist/src/index.js.map +1 -0
  82. package/dist/src/transcriber/ITranscriber.d.ts +12 -0
  83. package/dist/src/transcriber/ITranscriber.d.ts.map +1 -0
  84. package/dist/src/transcriber/ITranscriber.js +2 -0
  85. package/dist/src/transcriber/ITranscriber.js.map +1 -0
  86. package/dist/src/transcriber/WhisperTranscriber.d.ts +8 -0
  87. package/dist/src/transcriber/WhisperTranscriber.d.ts.map +1 -0
  88. package/dist/src/transcriber/WhisperTranscriber.js +35 -0
  89. package/dist/src/transcriber/WhisperTranscriber.js.map +1 -0
  90. package/dist/src/utils/clipboard.d.ts +11 -0
  91. package/dist/src/utils/clipboard.d.ts.map +1 -0
  92. package/dist/src/utils/clipboard.js +56 -0
  93. package/dist/src/utils/clipboard.js.map +1 -0
  94. package/dist/src/utils/srtParser.d.ts +6 -0
  95. package/dist/src/utils/srtParser.d.ts.map +1 -0
  96. package/dist/src/utils/srtParser.js +25 -0
  97. package/dist/src/utils/srtParser.js.map +1 -0
  98. package/dist/transcriber/ITranscriber.d.ts +12 -0
  99. package/dist/transcriber/ITranscriber.d.ts.map +1 -0
  100. package/dist/transcriber/ITranscriber.js +2 -0
  101. package/dist/transcriber/ITranscriber.js.map +1 -0
  102. package/dist/transcriber/WhisperTranscriber.d.ts +8 -0
  103. package/dist/transcriber/WhisperTranscriber.d.ts.map +1 -0
  104. package/dist/transcriber/WhisperTranscriber.js +35 -0
  105. package/dist/transcriber/WhisperTranscriber.js.map +1 -0
  106. package/dist/utils/clipboard.d.ts +11 -0
  107. package/dist/utils/clipboard.d.ts.map +1 -0
  108. package/dist/utils/clipboard.js +56 -0
  109. package/dist/utils/clipboard.js.map +1 -0
  110. package/dist/utils/srtParser.d.ts +6 -0
  111. package/dist/utils/srtParser.d.ts.map +1 -0
  112. package/dist/utils/srtParser.js +25 -0
  113. package/dist/utils/srtParser.js.map +1 -0
  114. package/package.json +51 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WhisperTranscriber.js","sourceRoot":"","sources":["../../../src/transcriber/WhisperTranscriber.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEnF,MAAM,OAAO,kBAAkB;IACrB,MAAM,CAAS;IAEvB,YAAY,MAAc;QACxB,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC;YACvB,MAAM;SACP,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,UAAU,CACd,aAAqB,EACrB,WAAyB,YAAY,CAAC,OAAO,EAC7C,SAA8B,mBAAmB,CAAC,IAAI,EACtD,MAAe,EACf,UAAqC;QAErC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,yBAAyB,aAAa,EAAE,CAAC,CAAC;QAC5D,CAAC;QAED,IAAI,CAAC;YACH,UAAU,EAAE,CAAC,2BAA2B,CAAC,CAAC;YAC1C,MAAM,UAAU,GAAG,EAAE,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC;YAEtD,UAAU,EAAE,CAAC,mEAAmE,CAAC,CAAC;YAClF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC;gBAC7D,IAAI,EAAE,UAAU;gBAChB,KAAK,EAAE,aAAa;gBACpB,QAAQ;gBACR,eAAe,EAAE,MAAM;gBACvB,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC9B,CAAC,CAAC;YAEH,UAAU,EAAE,CAAC,gCAAgC,CAAC,CAAC;YAC/C,OAAO,QAA6B,CAAC;QACvC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;YACrD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Copies text to the system clipboard asynchronously.
3
+ * Supports Linux (xclip / xsel), macOS (pbcopy) and Windows (clip).
4
+ * Fails silently if no clipboard tool is available.
5
+ *
6
+ * We use spawn instead of execSync because tools like xclip on Linux
7
+ * can fork into the background and keep the process alive, which causes
8
+ * execSync to hang forever and freeze the application UI.
9
+ */
10
+ export declare function copyTextToClipboard(text: string): void;
11
+ //# sourceMappingURL=clipboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard.d.ts","sourceRoot":"","sources":["../../../src/utils/clipboard.ts"],"names":[],"mappings":"AAiBA;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAkCtD"}
@@ -0,0 +1,56 @@
1
+ import { spawn } from 'child_process';
2
+ import os from 'os';
3
+ const PLATFORM_MAC = 'darwin';
4
+ const PLATFORM_WIN = 'win32';
5
+ const CMD_MAC = 'pbcopy';
6
+ const CMD_WIN = 'clip';
7
+ const CMD_LINUX_PRIMARY = 'xclip';
8
+ const CMD_LINUX_SECONDARY = 'xsel';
9
+ const ARGS_LINUX_PRIMARY = ['-selection', 'clipboard'];
10
+ const ARGS_LINUX_SECONDARY = ['--clipboard', '--input'];
11
+ const STDIO_IGNORE = ['pipe', 'ignore', 'ignore'];
12
+ const EVENT_ERROR = 'error';
13
+ /**
14
+ * Copies text to the system clipboard asynchronously.
15
+ * Supports Linux (xclip / xsel), macOS (pbcopy) and Windows (clip).
16
+ * Fails silently if no clipboard tool is available.
17
+ *
18
+ * We use spawn instead of execSync because tools like xclip on Linux
19
+ * can fork into the background and keep the process alive, which causes
20
+ * execSync to hang forever and freeze the application UI.
21
+ */
22
+ export function copyTextToClipboard(text) {
23
+ const platform = os.platform();
24
+ const getCmdArgs = () => {
25
+ if (platform === PLATFORM_MAC)
26
+ return { cmd: CMD_MAC, args: [] };
27
+ if (platform === PLATFORM_WIN)
28
+ return { cmd: CMD_WIN, args: [] };
29
+ return { cmd: CMD_LINUX_PRIMARY, args: ARGS_LINUX_PRIMARY };
30
+ };
31
+ const { cmd, args } = getCmdArgs();
32
+ try {
33
+ const proc = spawn(cmd, args, { stdio: STDIO_IGNORE });
34
+ proc.on(EVENT_ERROR, () => {
35
+ if (cmd === CMD_LINUX_PRIMARY) {
36
+ // Fallback to xsel if xclip fails on Linux
37
+ const fallback = spawn(CMD_LINUX_SECONDARY, ARGS_LINUX_SECONDARY, {
38
+ stdio: STDIO_IGNORE,
39
+ });
40
+ fallback.on(EVENT_ERROR, () => { });
41
+ if (fallback.stdin) {
42
+ fallback.stdin.write(text);
43
+ fallback.stdin.end();
44
+ }
45
+ }
46
+ });
47
+ if (proc.stdin) {
48
+ proc.stdin.write(text);
49
+ proc.stdin.end();
50
+ }
51
+ }
52
+ catch {
53
+ // Silently ignore clipboard errors
54
+ }
55
+ }
56
+ //# sourceMappingURL=clipboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard.js","sourceRoot":"","sources":["../../../src/utils/clipboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAqB,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB,MAAM,YAAY,GAAG,QAAQ,CAAC;AAC9B,MAAM,YAAY,GAAG,OAAO,CAAC;AAE7B,MAAM,OAAO,GAAG,QAAQ,CAAC;AACzB,MAAM,OAAO,GAAG,MAAM,CAAC;AACvB,MAAM,iBAAiB,GAAG,OAAO,CAAC;AAClC,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAEnC,MAAM,kBAAkB,GAAG,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;AACvD,MAAM,oBAAoB,GAAG,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;AAExD,MAAM,YAAY,GAAiB,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAChE,MAAM,WAAW,GAAG,OAAO,CAAC;AAE5B;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,MAAM,QAAQ,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;IAC/B,MAAM,UAAU,GAAG,GAAoC,EAAE;QACvD,IAAI,QAAQ,KAAK,YAAY;YAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;QACjE,IAAI,QAAQ,KAAK,YAAY;YAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;QACjE,OAAO,EAAE,GAAG,EAAE,iBAAiB,EAAE,IAAI,EAAE,kBAAkB,EAAE,CAAC;IAC9D,CAAC,CAAC;IAEF,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,UAAU,EAAE,CAAC;IAEnC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;QAEvD,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,GAAG,EAAE;YACxB,IAAI,GAAG,KAAK,iBAAiB,EAAE,CAAC;gBAC9B,2CAA2C;gBAC3C,MAAM,QAAQ,GAAG,KAAK,CAAC,mBAAmB,EAAE,oBAAoB,EAAE;oBAChE,KAAK,EAAE,YAAY;iBACpB,CAAC,CAAC;gBACH,QAAQ,CAAC,EAAE,CAAC,WAAW,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;gBACnC,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;oBACnB,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBAC3B,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;gBACvB,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACvB,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QACnB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,mCAAmC;IACrC,CAAC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Extracts the raw spoken text from an SRT file, joining all subtitle
3
+ * segments into a single continuous paragraph separated by spaces.
4
+ */
5
+ export declare function extractTextFromSrt(srtContent: string): string;
6
+ //# sourceMappingURL=srtParser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"srtParser.d.ts","sourceRoot":"","sources":["../../../src/utils/srtParser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAmB7D"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Extracts the raw spoken text from an SRT file, joining all subtitle
3
+ * segments into a single continuous paragraph separated by spaces.
4
+ */
5
+ export function extractTextFromSrt(srtContent) {
6
+ if (!srtContent)
7
+ return '';
8
+ const lines = srtContent.split(/\r?\n/);
9
+ const textLines = [];
10
+ for (const line of lines) {
11
+ const trimmed = line.trim();
12
+ if (!trimmed)
13
+ continue;
14
+ // Skip subtitle index numbers
15
+ if (/^\d+$/.test(trimmed))
16
+ continue;
17
+ // Skip timestamp lines (e.g. 00:00:00,000 --> 00:00:02,000)
18
+ if (trimmed.includes('-->'))
19
+ continue;
20
+ textLines.push(trimmed);
21
+ }
22
+ // Join as one paragraph — segments are separated by a single space
23
+ return textLines.join(' ');
24
+ }
25
+ //# sourceMappingURL=srtParser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"srtParser.js","sourceRoot":"","sources":["../../../src/utils/srtParser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,UAAkB;IACnD,IAAI,CAAC,UAAU;QAAE,OAAO,EAAE,CAAC;IAE3B,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACxC,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,8BAA8B;QAC9B,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,SAAS;QACpC,4DAA4D;QAC5D,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,SAAS;QAEtC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC1B,CAAC;IAED,mEAAmE;IACnE,OAAO,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { LanguageCode, TranscriptionFormat } from '../constants';
2
+ export interface ITranscriber {
3
+ /**
4
+ * Transcribes an audio file and returns the result as a string.
5
+ * @param audioFilePath Absolute or relative path to the audio file.
6
+ * @param language Language code for the audio content.
7
+ * @param format Desired response format.
8
+ * @param prompt Optional context hint / glossary for the model.
9
+ */
10
+ transcribe(audioFilePath: string, language: LanguageCode, format: TranscriptionFormat, prompt?: string, onProgress?: (status: string) => void): Promise<string>;
11
+ }
12
+ //# sourceMappingURL=ITranscriber.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ITranscriber.d.ts","sourceRoot":"","sources":["../../src/transcriber/ITranscriber.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAEtE,MAAM,WAAW,YAAY;IAC3B;;;;;;OAMG;IACH,UAAU,CACR,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,YAAY,EACtB,MAAM,EAAE,mBAAmB,EAC3B,MAAM,CAAC,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GACpC,OAAO,CAAC,MAAM,CAAC,CAAC;CACpB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ITranscriber.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ITranscriber.js","sourceRoot":"","sources":["../../src/transcriber/ITranscriber.ts"],"names":[],"mappings":""}
@@ -0,0 +1,8 @@
1
+ import type { ITranscriber } from './ITranscriber.js';
2
+ import { LanguageCode, TranscriptionFormat } from '../constants.js';
3
+ export declare class WhisperTranscriber implements ITranscriber {
4
+ private openai;
5
+ constructor(apiKey: string);
6
+ transcribe(audioFilePath: string, language?: LanguageCode, format?: TranscriptionFormat, prompt?: string, onProgress?: (status: string) => void): Promise<string>;
7
+ }
8
+ //# sourceMappingURL=WhisperTranscriber.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WhisperTranscriber.d.ts","sourceRoot":"","sources":["../../src/transcriber/WhisperTranscriber.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAiB,MAAM,iBAAiB,CAAC;AAEnF,qBAAa,kBAAmB,YAAW,YAAY;IACrD,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,MAAM;IAMpB,UAAU,CACd,aAAa,EAAE,MAAM,EACrB,QAAQ,GAAE,YAAmC,EAC7C,MAAM,GAAE,mBAA8C,EACtD,MAAM,CAAC,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GACpC,OAAO,CAAC,MAAM,CAAC;CAyBnB"}
@@ -0,0 +1,35 @@
1
+ import fs from 'fs';
2
+ import OpenAI from 'openai';
3
+ import { LanguageCode, TranscriptionFormat, WHISPER_MODEL } from '../constants.js';
4
+ export class WhisperTranscriber {
5
+ openai;
6
+ constructor(apiKey) {
7
+ this.openai = new OpenAI({
8
+ apiKey,
9
+ });
10
+ }
11
+ async transcribe(audioFilePath, language = LanguageCode.ENGLISH, format = TranscriptionFormat.TEXT, prompt, onProgress) {
12
+ if (!fs.existsSync(audioFilePath)) {
13
+ throw new Error(`Audio file not found: ${audioFilePath}`);
14
+ }
15
+ try {
16
+ onProgress?.('Preparing audio stream...');
17
+ const fileStream = fs.createReadStream(audioFilePath);
18
+ onProgress?.('Sending request to OpenAI Whisper API (uploading & processing)...');
19
+ const response = await this.openai.audio.transcriptions.create({
20
+ file: fileStream,
21
+ model: WHISPER_MODEL,
22
+ language,
23
+ response_format: format,
24
+ ...(prompt ? { prompt } : {}),
25
+ });
26
+ onProgress?.('Response received from OpenAI.');
27
+ return response;
28
+ }
29
+ catch (error) {
30
+ console.error('Whisper transcription error:', error);
31
+ throw error;
32
+ }
33
+ }
34
+ }
35
+ //# sourceMappingURL=WhisperTranscriber.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WhisperTranscriber.js","sourceRoot":"","sources":["../../src/transcriber/WhisperTranscriber.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEnF,MAAM,OAAO,kBAAkB;IACrB,MAAM,CAAS;IAEvB,YAAY,MAAc;QACxB,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC;YACvB,MAAM;SACP,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,UAAU,CACd,aAAqB,EACrB,WAAyB,YAAY,CAAC,OAAO,EAC7C,SAA8B,mBAAmB,CAAC,IAAI,EACtD,MAAe,EACf,UAAqC;QAErC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,yBAAyB,aAAa,EAAE,CAAC,CAAC;QAC5D,CAAC;QAED,IAAI,CAAC;YACH,UAAU,EAAE,CAAC,2BAA2B,CAAC,CAAC;YAC1C,MAAM,UAAU,GAAG,EAAE,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC;YAEtD,UAAU,EAAE,CAAC,mEAAmE,CAAC,CAAC;YAClF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC;gBAC7D,IAAI,EAAE,UAAU;gBAChB,KAAK,EAAE,aAAa;gBACpB,QAAQ;gBACR,eAAe,EAAE,MAAM;gBACvB,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC9B,CAAC,CAAC;YAEH,UAAU,EAAE,CAAC,gCAAgC,CAAC,CAAC;YAC/C,OAAO,QAA6B,CAAC;QACvC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;YACrD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Copies text to the system clipboard asynchronously.
3
+ * Supports Linux (xclip / xsel), macOS (pbcopy) and Windows (clip).
4
+ * Fails silently if no clipboard tool is available.
5
+ *
6
+ * We use spawn instead of execSync because tools like xclip on Linux
7
+ * can fork into the background and keep the process alive, which causes
8
+ * execSync to hang forever and freeze the application UI.
9
+ */
10
+ export declare function copyTextToClipboard(text: string): void;
11
+ //# sourceMappingURL=clipboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard.d.ts","sourceRoot":"","sources":["../../src/utils/clipboard.ts"],"names":[],"mappings":"AAiBA;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAkCtD"}
@@ -0,0 +1,56 @@
1
+ import { spawn } from 'child_process';
2
+ import os from 'os';
3
+ const PLATFORM_MAC = 'darwin';
4
+ const PLATFORM_WIN = 'win32';
5
+ const CMD_MAC = 'pbcopy';
6
+ const CMD_WIN = 'clip';
7
+ const CMD_LINUX_PRIMARY = 'xclip';
8
+ const CMD_LINUX_SECONDARY = 'xsel';
9
+ const ARGS_LINUX_PRIMARY = ['-selection', 'clipboard'];
10
+ const ARGS_LINUX_SECONDARY = ['--clipboard', '--input'];
11
+ const STDIO_IGNORE = ['pipe', 'ignore', 'ignore'];
12
+ const EVENT_ERROR = 'error';
13
+ /**
14
+ * Copies text to the system clipboard asynchronously.
15
+ * Supports Linux (xclip / xsel), macOS (pbcopy) and Windows (clip).
16
+ * Fails silently if no clipboard tool is available.
17
+ *
18
+ * We use spawn instead of execSync because tools like xclip on Linux
19
+ * can fork into the background and keep the process alive, which causes
20
+ * execSync to hang forever and freeze the application UI.
21
+ */
22
+ export function copyTextToClipboard(text) {
23
+ const platform = os.platform();
24
+ const getCmdArgs = () => {
25
+ if (platform === PLATFORM_MAC)
26
+ return { cmd: CMD_MAC, args: [] };
27
+ if (platform === PLATFORM_WIN)
28
+ return { cmd: CMD_WIN, args: [] };
29
+ return { cmd: CMD_LINUX_PRIMARY, args: ARGS_LINUX_PRIMARY };
30
+ };
31
+ const { cmd, args } = getCmdArgs();
32
+ try {
33
+ const proc = spawn(cmd, args, { stdio: STDIO_IGNORE });
34
+ proc.on(EVENT_ERROR, () => {
35
+ if (cmd === CMD_LINUX_PRIMARY) {
36
+ // Fallback to xsel if xclip fails on Linux
37
+ const fallback = spawn(CMD_LINUX_SECONDARY, ARGS_LINUX_SECONDARY, {
38
+ stdio: STDIO_IGNORE,
39
+ });
40
+ fallback.on(EVENT_ERROR, () => { });
41
+ if (fallback.stdin) {
42
+ fallback.stdin.write(text);
43
+ fallback.stdin.end();
44
+ }
45
+ }
46
+ });
47
+ if (proc.stdin) {
48
+ proc.stdin.write(text);
49
+ proc.stdin.end();
50
+ }
51
+ }
52
+ catch {
53
+ // Silently ignore clipboard errors
54
+ }
55
+ }
56
+ //# sourceMappingURL=clipboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard.js","sourceRoot":"","sources":["../../src/utils/clipboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAqB,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB,MAAM,YAAY,GAAG,QAAQ,CAAC;AAC9B,MAAM,YAAY,GAAG,OAAO,CAAC;AAE7B,MAAM,OAAO,GAAG,QAAQ,CAAC;AACzB,MAAM,OAAO,GAAG,MAAM,CAAC;AACvB,MAAM,iBAAiB,GAAG,OAAO,CAAC;AAClC,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAEnC,MAAM,kBAAkB,GAAG,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;AACvD,MAAM,oBAAoB,GAAG,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;AAExD,MAAM,YAAY,GAAiB,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAChE,MAAM,WAAW,GAAG,OAAO,CAAC;AAE5B;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,MAAM,QAAQ,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;IAC/B,MAAM,UAAU,GAAG,GAAoC,EAAE;QACvD,IAAI,QAAQ,KAAK,YAAY;YAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;QACjE,IAAI,QAAQ,KAAK,YAAY;YAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;QACjE,OAAO,EAAE,GAAG,EAAE,iBAAiB,EAAE,IAAI,EAAE,kBAAkB,EAAE,CAAC;IAC9D,CAAC,CAAC;IAEF,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,UAAU,EAAE,CAAC;IAEnC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;QAEvD,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,GAAG,EAAE;YACxB,IAAI,GAAG,KAAK,iBAAiB,EAAE,CAAC;gBAC9B,2CAA2C;gBAC3C,MAAM,QAAQ,GAAG,KAAK,CAAC,mBAAmB,EAAE,oBAAoB,EAAE;oBAChE,KAAK,EAAE,YAAY;iBACpB,CAAC,CAAC;gBACH,QAAQ,CAAC,EAAE,CAAC,WAAW,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;gBACnC,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;oBACnB,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBAC3B,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;gBACvB,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACvB,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QACnB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,mCAAmC;IACrC,CAAC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Extracts the raw spoken text from an SRT file, joining all subtitle
3
+ * segments into a single continuous paragraph separated by spaces.
4
+ */
5
+ export declare function extractTextFromSrt(srtContent: string): string;
6
+ //# sourceMappingURL=srtParser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"srtParser.d.ts","sourceRoot":"","sources":["../../src/utils/srtParser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAmB7D"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Extracts the raw spoken text from an SRT file, joining all subtitle
3
+ * segments into a single continuous paragraph separated by spaces.
4
+ */
5
+ export function extractTextFromSrt(srtContent) {
6
+ if (!srtContent)
7
+ return '';
8
+ const lines = srtContent.split(/\r?\n/);
9
+ const textLines = [];
10
+ for (const line of lines) {
11
+ const trimmed = line.trim();
12
+ if (!trimmed)
13
+ continue;
14
+ // Skip subtitle index numbers
15
+ if (/^\d+$/.test(trimmed))
16
+ continue;
17
+ // Skip timestamp lines (e.g. 00:00:00,000 --> 00:00:02,000)
18
+ if (trimmed.includes('-->'))
19
+ continue;
20
+ textLines.push(trimmed);
21
+ }
22
+ // Join as one paragraph — segments are separated by a single space
23
+ return textLines.join(' ');
24
+ }
25
+ //# sourceMappingURL=srtParser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"srtParser.js","sourceRoot":"","sources":["../../src/utils/srtParser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,UAAkB;IACnD,IAAI,CAAC,UAAU;QAAE,OAAO,EAAE,CAAC;IAE3B,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACxC,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,8BAA8B;QAC9B,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,SAAS;QACpC,4DAA4D;QAC5D,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,SAAS;QAEtC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC1B,CAAC;IAED,mEAAmE;IACnE,OAAO,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC"}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@bryan-gc/transcribe-cli",
3
+ "version": "1.0.0",
4
+ "description": "CLI tool to record audio and transcribe it using OpenAI Whisper",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "transcribe-cli": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "type": "module",
13
+ "scripts": {
14
+ "start": "tsx src/index.tsx",
15
+ "build": "tsc",
16
+ "prepublishOnly": "npm run build",
17
+ "lint": "eslint 'src/**/*.{ts,tsx}'",
18
+ "lint:fix": "eslint 'src/**/*.{ts,tsx}' --fix",
19
+ "format": "prettier --write 'src/**/*.{ts,tsx}'",
20
+ "format:check": "prettier --check 'src/**/*.{ts,tsx}'",
21
+ "test": "echo \"Error: no test specified\" && exit 1"
22
+ },
23
+ "keywords": [
24
+ "cli",
25
+ "transcribe",
26
+ "openai",
27
+ "whisper",
28
+ "audio"
29
+ ],
30
+ "author": "bryan-gc",
31
+ "license": "ISC",
32
+ "dependencies": {
33
+ "@inkjs/ui": "^2.0.0",
34
+ "@types/node": "^25.7.0",
35
+ "ink": "^7.0.3",
36
+ "openai": "^6.37.0",
37
+ "react": "^19.2.6",
38
+ "tsx": "^4.22.0",
39
+ "typescript": "^6.0.3"
40
+ },
41
+ "devDependencies": {
42
+ "@types/react": "^19.2.14",
43
+ "eslint": "^9.39.4",
44
+ "eslint-config-prettier": "^10.1.8",
45
+ "eslint-plugin-prettier": "^5.5.5",
46
+ "eslint-plugin-react": "^7.37.5",
47
+ "eslint-plugin-react-hooks": "^7.1.1",
48
+ "prettier": "^3.8.3",
49
+ "typescript-eslint": "^8.59.3"
50
+ }
51
+ }