s2t 0.1.8__py3-none-any.whl → 0.1.10__py3-none-any.whl
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.
- s2t/cli.py +16 -5
- s2t/recorder.py +25 -11
- {s2t-0.1.8.dist-info → s2t-0.1.10.dist-info}/METADATA +6 -5
- {s2t-0.1.8.dist-info → s2t-0.1.10.dist-info}/RECORD +7 -7
- {s2t-0.1.8.dist-info → s2t-0.1.10.dist-info}/WHEEL +0 -0
- {s2t-0.1.8.dist-info → s2t-0.1.10.dist-info}/entry_points.txt +0 -0
- {s2t-0.1.8.dist-info → s2t-0.1.10.dist-info}/top_level.txt +0 -0
s2t/cli.py
CHANGED
@@ -99,7 +99,8 @@ def run_session(opts: SessionOptions) -> int:
|
|
99
99
|
detected_lang_holder=detected_lang,
|
100
100
|
)
|
101
101
|
|
102
|
-
|
102
|
+
# Include split cause per chunk: "space" (manual), "pause" (auto), "finish" (final)
|
103
|
+
tx_q: queue.Queue[tuple[int, Path, int, float, str]] = queue.Queue()
|
103
104
|
cumulative_text = ""
|
104
105
|
next_to_emit = 1
|
105
106
|
pending: dict[int, str] = {}
|
@@ -150,8 +151,9 @@ def run_session(opts: SessionOptions) -> int:
|
|
150
151
|
model = engine.resolve_model(fut)
|
151
152
|
nonlocal cumulative_text, next_to_emit
|
152
153
|
finished_texts: dict[int, str] = {}
|
154
|
+
causes: dict[int, str] = {}
|
153
155
|
while True:
|
154
|
-
idx, path, frames, offset = tx_q.get()
|
156
|
+
idx, path, frames, offset, cause = tx_q.get()
|
155
157
|
if idx == -1:
|
156
158
|
break
|
157
159
|
# If in spoken-prompt mode, ensure we don't process payload chunks before prompt is done
|
@@ -174,12 +176,21 @@ def run_session(opts: SessionOptions) -> int:
|
|
174
176
|
results.append(res)
|
175
177
|
offsets.append(offset)
|
176
178
|
pending[idx] = text_i
|
179
|
+
# Track cause for formatting when emitting in-order
|
180
|
+
# cause is one of: "space", "pause", "finish" (or empty for sentinel)
|
181
|
+
# Default to "pause" if unknown to avoid extra blank lines.
|
182
|
+
causes[idx] = cause or "pause"
|
177
183
|
while next_to_emit in pending:
|
178
184
|
out = pending.pop(next_to_emit)
|
179
185
|
if out:
|
186
|
+
# Determine separator based on split cause
|
187
|
+
sep = "\n\n" if causes.get(next_to_emit) == "space" else "\n"
|
180
188
|
print(out)
|
181
|
-
|
182
|
-
|
189
|
+
if causes.get(next_to_emit) == "space":
|
190
|
+
# Space -> insert a blank line after the chunk
|
191
|
+
print("")
|
192
|
+
# Build cumulative text with matching separators
|
193
|
+
cumulative_text += out if not cumulative_text else (sep + out)
|
183
194
|
try:
|
184
195
|
copy_to_clipboard(cumulative_text)
|
185
196
|
except Exception:
|
@@ -446,7 +457,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
446
457
|
parser.add_argument(
|
447
458
|
"--silence-sec",
|
448
459
|
type=float,
|
449
|
-
default=
|
460
|
+
default=2.0,
|
450
461
|
help="Auto-split when continuous silence >= this many seconds (0 disables)",
|
451
462
|
)
|
452
463
|
parser.add_argument(
|
s2t/recorder.py
CHANGED
@@ -41,7 +41,7 @@ class Recorder:
|
|
41
41
|
|
42
42
|
def run(
|
43
43
|
self,
|
44
|
-
tx_queue: queue.Queue[tuple[int, Path, int, float]],
|
44
|
+
tx_queue: queue.Queue[tuple[int, Path, int, float, str]],
|
45
45
|
) -> tuple[list[Path], list[int], list[float]]:
|
46
46
|
import platform
|
47
47
|
import termios
|
@@ -224,7 +224,7 @@ class Recorder:
|
|
224
224
|
threshold_rms = 0.015 # conservative RMS threshold for float32 [-1,1]
|
225
225
|
split_cooldown_sec = 0.2
|
226
226
|
|
227
|
-
def _do_split() -> None:
|
227
|
+
def _do_split(cause: str) -> None:
|
228
228
|
nonlocal fh, frames_written, cur_path, chunk_index, offset_seconds_total
|
229
229
|
fh.flush()
|
230
230
|
fh.close()
|
@@ -239,7 +239,9 @@ class Recorder:
|
|
239
239
|
f"Saved chunk: {cur_path.name} ({dur:.2f}s)",
|
240
240
|
file=sys.stderr,
|
241
241
|
)
|
242
|
-
|
242
|
+
# Include split cause so downstream can format output accordingly
|
243
|
+
# cause: "space" (manual split) or "pause" (auto-split)
|
244
|
+
tx_queue.put((chunk_index, cur_path, frames_written, chunk_offsets[-1], cause))
|
243
245
|
else:
|
244
246
|
try:
|
245
247
|
cur_path.unlink(missing_ok=True)
|
@@ -270,8 +272,13 @@ class Recorder:
|
|
270
272
|
try:
|
271
273
|
while True:
|
272
274
|
cmd = ctrl_q.get_nowait()
|
273
|
-
if cmd == "
|
274
|
-
_do_split()
|
275
|
+
if cmd == "split_manual":
|
276
|
+
_do_split("space")
|
277
|
+
# Reset silence tracking on manual split
|
278
|
+
silent_frames_run = 0
|
279
|
+
seen_non_silent = False
|
280
|
+
elif cmd == "split_auto":
|
281
|
+
_do_split("pause")
|
275
282
|
# Reset silence tracking on manual split
|
276
283
|
silent_frames_run = 0
|
277
284
|
seen_non_silent = False
|
@@ -289,15 +296,22 @@ class Recorder:
|
|
289
296
|
f"Saved chunk: {cur_path.name} ({dur:.2f}s)",
|
290
297
|
file=sys.stderr,
|
291
298
|
)
|
299
|
+
# Final chunk – mark cause as "finish" so downstream can avoid extra blank spacing
|
292
300
|
tx_queue.put(
|
293
|
-
(
|
301
|
+
(
|
302
|
+
chunk_index,
|
303
|
+
cur_path,
|
304
|
+
frames_written,
|
305
|
+
chunk_offsets[-1],
|
306
|
+
"finish",
|
307
|
+
)
|
294
308
|
)
|
295
309
|
else:
|
296
310
|
try:
|
297
311
|
cur_path.unlink(missing_ok=True)
|
298
312
|
except Exception:
|
299
313
|
pass
|
300
|
-
tx_queue.put((-1, Path(), 0, 0.0))
|
314
|
+
tx_queue.put((-1, Path(), 0, 0.0, ""))
|
301
315
|
return
|
302
316
|
except queue.Empty:
|
303
317
|
pass
|
@@ -348,12 +362,12 @@ class Recorder:
|
|
348
362
|
file=sys.stderr,
|
349
363
|
)
|
350
364
|
last_split_time = time.perf_counter()
|
351
|
-
# Queue
|
352
|
-
ctrl_q.put("
|
365
|
+
# Queue an auto split for the next control phase
|
366
|
+
ctrl_q.put("split_auto")
|
353
367
|
# Reset silence tracking now to avoid cascaded triggers
|
354
368
|
silent_frames_run = 0
|
355
369
|
seen_non_silent = False
|
356
|
-
tx_queue.put((-1, Path(), 0, 0.0))
|
370
|
+
tx_queue.put((-1, Path(), 0, 0.0, ""))
|
357
371
|
|
358
372
|
def cb(indata: Any, frames: int, time_info: Any, status: Any) -> None:
|
359
373
|
if status:
|
@@ -384,7 +398,7 @@ class Recorder:
|
|
384
398
|
except queue.Empty:
|
385
399
|
continue
|
386
400
|
if evt == "SPACE":
|
387
|
-
ctrl_q.put("
|
401
|
+
ctrl_q.put("split_manual")
|
388
402
|
elif evt == "ENTER":
|
389
403
|
ctrl_q.put("finish")
|
390
404
|
break
|
@@ -1,15 +1,16 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: s2t
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.10
|
4
4
|
Summary: Speech to Text (s2t): Record audio, run Whisper, export formats, and copy transcript to clipboard.
|
5
5
|
Author: Maintainers
|
6
6
|
License-Expression: LicenseRef-Proprietary
|
7
7
|
Classifier: Programming Language :: Python :: 3
|
8
8
|
Classifier: Programming Language :: Python :: 3 :: Only
|
9
9
|
Classifier: Programming Language :: Python :: 3.11
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
10
11
|
Classifier: Environment :: Console
|
11
12
|
Classifier: Operating System :: OS Independent
|
12
|
-
Requires-Python:
|
13
|
+
Requires-Python: <3.13,>=3.11
|
13
14
|
Description-Content-Type: text/markdown
|
14
15
|
Requires-Dist: sounddevice>=0.4.6
|
15
16
|
Requires-Dist: soundfile>=0.12.1
|
@@ -36,7 +37,7 @@ Record audio from your microphone, run Whisper to transcribe it, export common f
|
|
36
37
|
- Editable: `pip install -e .`
|
37
38
|
- Standard: `pip install .`
|
38
39
|
|
39
|
-
Requirements: Python 3.11
|
40
|
+
Requirements: Python 3.11–3.12. No mandatory external binaries. ffmpeg is optional (only for MP3 encoding/decoding).
|
40
41
|
|
41
42
|
System requirements (Linux)
|
42
43
|
- Some environments need system libraries for audio I/O:
|
@@ -91,5 +92,5 @@ Notes on models
|
|
91
92
|
- The name `turbo` refers to OpenAI’s hosted model family and is not provided by the local `whisper` CLI. If you pass `-m turbo`, the command may fail; choose a supported local model instead.
|
92
93
|
|
93
94
|
## Development & Release
|
94
|
-
-
|
95
|
-
-
|
95
|
+
- For developer setup and contribution guidelines, see `CONTRIBUTING.md`.
|
96
|
+
- For the release process, see `docs/RELEASING.md`.
|
@@ -1,16 +1,16 @@
|
|
1
1
|
s2t/__init__.py,sha256=wV4E9i-7KrUn1dOtLUQB3ZGEKx9gRWH3hPHlpw-ZdWc,332
|
2
|
-
s2t/cli.py,sha256=
|
2
|
+
s2t/cli.py,sha256=1t3fchtywFaeuPONp-B3FmSzBnSxJRvP6jx9AS-b1Ok,21351
|
3
3
|
s2t/config.py,sha256=uw4CZSSXmUvnlOrqBGR1Rcq-WdXucHj3KICRcCb_pkU,485
|
4
4
|
s2t/outputs.py,sha256=Lo8VcARZ7QPuuQQNu8myD5J4c4NO1Rs0L1DLnzLe9tM,1546
|
5
5
|
s2t/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
6
|
-
s2t/recorder.py,sha256=
|
6
|
+
s2t/recorder.py,sha256=Z3Hn8l1xLY7XzLR6zqMYulTBfTRNWD-zqwk_V5x18Sc,19228
|
7
7
|
s2t/types.py,sha256=jBiRN-tr0qVw-lhaXvnsyKrVGDyLkqEbxs9qkQ6qGqI,339
|
8
8
|
s2t/utils.py,sha256=YU6YhiuONmqhrKte4DY5tiC5PP-yFExJMMBzFUiA8qA,3416
|
9
9
|
s2t/whisper_engine.py,sha256=x-V7ST9e3JnwMWdbMh4C7dHjA420jaOtXH2-igeh7vc,6492
|
10
10
|
s2t/translator/__init__.py,sha256=K-MKves7kZ4-62POfrmWeOcBaTjsTzeFSu8QNHqYuus,239
|
11
11
|
s2t/translator/argos_backend.py,sha256=VW_OYFFBuNZgcWM-fbvR6XGokuxS2fptkCMFIO9MD1I,19068
|
12
|
-
s2t-0.1.
|
13
|
-
s2t-0.1.
|
14
|
-
s2t-0.1.
|
15
|
-
s2t-0.1.
|
16
|
-
s2t-0.1.
|
12
|
+
s2t-0.1.10.dist-info/METADATA,sha256=ViJoiYC5WG_aLaRTLXedQ-o8TwyBQ_sCN126u9o96lY,5475
|
13
|
+
s2t-0.1.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
14
|
+
s2t-0.1.10.dist-info/entry_points.txt,sha256=JISIUlZAJ3DX1dB6zT3X_E3vcXI-eWEQKwHiT35fPKs,37
|
15
|
+
s2t-0.1.10.dist-info/top_level.txt,sha256=o8N0JcuHdIrfX3iGHvntHiDC2XgN7__joyNu08ZOh0s,4
|
16
|
+
s2t-0.1.10.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|