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 CHANGED
@@ -99,7 +99,8 @@ def run_session(opts: SessionOptions) -> int:
99
99
  detected_lang_holder=detected_lang,
100
100
  )
101
101
 
102
- tx_q: queue.Queue[tuple[int, Path, int, float]] = queue.Queue()
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
- print("")
182
- cumulative_text += out if not cumulative_text else ("\n\n" + out)
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=1.0,
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
- tx_queue.put((chunk_index, cur_path, frames_written, chunk_offsets[-1]))
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 == "split":
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
- (chunk_index, cur_path, frames_written, chunk_offsets[-1])
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 a split for the next control phase
352
- ctrl_q.put("split")
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("split")
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.8
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: >=3.11
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+. No mandatory external binaries. ffmpeg is optional (only for MP3 encoding/decoding).
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
- - Für Entwickler-Setup und Beitragshinweise siehe `CONTRIBUTING.md`.
95
- - Für den Release-Prozess siehe `docs/RELEASING.md`.
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=p6lvizzW1T1Y-_ykJ8FdPaBVvZS_no7OgRv4gWlZ95s,20572
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=tVUGwnHnlwQM9UtdHUlQIZ3GubCrop9suZYxc9yHHmE,18370
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.8.dist-info/METADATA,sha256=nWWknxaQYnUNkLMhFj7czAcGNnva7OcGX4iwNKB9cmY,5409
13
- s2t-0.1.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
14
- s2t-0.1.8.dist-info/entry_points.txt,sha256=JISIUlZAJ3DX1dB6zT3X_E3vcXI-eWEQKwHiT35fPKs,37
15
- s2t-0.1.8.dist-info/top_level.txt,sha256=o8N0JcuHdIrfX3iGHvntHiDC2XgN7__joyNu08ZOh0s,4
16
- s2t-0.1.8.dist-info/RECORD,,
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