talks-reducer 0.3.1__tar.gz → 0.3.2__tar.gz

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 (24) hide show
  1. {talks_reducer-0.3.1/talks_reducer.egg-info → talks_reducer-0.3.2}/PKG-INFO +8 -4
  2. {talks_reducer-0.3.1 → talks_reducer-0.3.2}/README.md +7 -3
  3. {talks_reducer-0.3.1 → talks_reducer-0.3.2}/pyproject.toml +1 -1
  4. {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer/cli.py +21 -17
  5. {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer/ffmpeg.py +7 -6
  6. {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer/gui.py +2 -2
  7. {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer/pipeline.py +1 -1
  8. {talks_reducer-0.3.1 → talks_reducer-0.3.2/talks_reducer.egg-info}/PKG-INFO +8 -4
  9. {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer.egg-info/SOURCES.txt +1 -0
  10. talks_reducer-0.3.2/tests/test_cli.py +78 -0
  11. {talks_reducer-0.3.1 → talks_reducer-0.3.2}/LICENSE +0 -0
  12. {talks_reducer-0.3.1 → talks_reducer-0.3.2}/setup.cfg +0 -0
  13. {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer/__init__.py +0 -0
  14. {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer/__main__.py +0 -0
  15. {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer/audio.py +0 -0
  16. {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer/chunks.py +0 -0
  17. {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer/models.py +0 -0
  18. {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer/progress.py +0 -0
  19. {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer.egg-info/dependency_links.txt +0 -0
  20. {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer.egg-info/entry_points.txt +0 -0
  21. {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer.egg-info/requires.txt +0 -0
  22. {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer.egg-info/top_level.txt +0 -0
  23. {talks_reducer-0.3.1 → talks_reducer-0.3.2}/tests/test_audio.py +0 -0
  24. {talks_reducer-0.3.1 → talks_reducer-0.3.2}/tests/test_pipeline_service.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: talks-reducer
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: CLI for speeding up long-form talks by removing silence
5
5
  Author: Talks Reducer Maintainers
6
6
  License-Expression: MIT
@@ -32,6 +32,10 @@ project was renamed from **jumpcutter** to emphasize its focus on conference tal
32
32
  - 1h 19m, 751 MB — Talks Reducer
33
33
  - 1h 19m, 171 MB — Talks Reducer `--small`
34
34
 
35
+ ## Changelog
36
+
37
+ See [CHANGELOG.md](CHANGELOG.md).
38
+
35
39
  ## Install GUI (Windows, macOS)
36
40
  Go to the [releases page](https://github.com/popstas/talks-reducer/releases) and download the appropriate artifact:
37
41
 
@@ -50,9 +54,9 @@ pip install talks-reducer
50
54
  The `--small` preset applies a 720p video scale and 128 kbps audio bitrate, making it useful for sharing talks over constrained
51
55
  connections. Without `--small`, the script aims to preserve original quality while removing silence.
52
56
 
53
- > **Tip:** Running `talks-reducer-gui` without arguments opens the Tkinter interface. Passing regular CLI options (for example,
54
- > `talks-reducer-gui --small input.mp4`) now executes the command-line pipeline, so you can keep a single shortcut for both
55
- > workflows.
57
+ > **Tip:** The `talks-reducer` and `talks-reducer-gui` commands now behave the same way: launching them without arguments opens
58
+ > the Tkinter interface, while passing regular CLI options (for example, `talks-reducer --small input.mp4`) executes the
59
+ > command-line pipeline. You can keep a single shortcut for both workflows.
56
60
 
57
61
  When CUDA-capable hardware is available the pipeline leans on GPU encoders to keep export times low, but it still runs great on
58
62
  CPUs.
@@ -7,6 +7,10 @@ project was renamed from **jumpcutter** to emphasize its focus on conference tal
7
7
  - 1h 19m, 751 MB — Talks Reducer
8
8
  - 1h 19m, 171 MB — Talks Reducer `--small`
9
9
 
10
+ ## Changelog
11
+
12
+ See [CHANGELOG.md](CHANGELOG.md).
13
+
10
14
  ## Install GUI (Windows, macOS)
11
15
  Go to the [releases page](https://github.com/popstas/talks-reducer/releases) and download the appropriate artifact:
12
16
 
@@ -25,9 +29,9 @@ pip install talks-reducer
25
29
  The `--small` preset applies a 720p video scale and 128 kbps audio bitrate, making it useful for sharing talks over constrained
26
30
  connections. Without `--small`, the script aims to preserve original quality while removing silence.
27
31
 
28
- > **Tip:** Running `talks-reducer-gui` without arguments opens the Tkinter interface. Passing regular CLI options (for example,
29
- > `talks-reducer-gui --small input.mp4`) now executes the command-line pipeline, so you can keep a single shortcut for both
30
- > workflows.
32
+ > **Tip:** The `talks-reducer` and `talks-reducer-gui` commands now behave the same way: launching them without arguments opens
33
+ > the Tkinter interface, while passing regular CLI options (for example, `talks-reducer --small input.mp4`) executes the
34
+ > command-line pipeline. You can keep a single shortcut for both workflows.
31
35
 
32
36
  When CUDA-capable hardware is available the pipeline leans on GPU encoders to keep export times low, but it still runs great on
33
37
  CPUs.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "talks-reducer"
7
- version = "0.3.1"
7
+ version = "0.3.2"
8
8
  description = "CLI for speeding up long-form talks by removing silence"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -6,6 +6,7 @@ import argparse
6
6
  import os
7
7
  import sys
8
8
  import time
9
+ from importlib import import_module
9
10
  from importlib.metadata import version
10
11
  from pathlib import Path
11
12
  from typing import Dict, List, Optional, Sequence
@@ -23,19 +24,19 @@ def _build_parser() -> argparse.ArgumentParser:
23
24
  parser = argparse.ArgumentParser(
24
25
  description="Modifies a video file to play at different speeds when there is sound vs. silence.",
25
26
  )
26
-
27
+
27
28
  # Add version argument
28
29
  try:
29
30
  pkg_version = version("talks-reducer")
30
31
  except Exception:
31
32
  pkg_version = "unknown"
32
-
33
+
33
34
  parser.add_argument(
34
35
  "--version",
35
36
  action="version",
36
37
  version=f"talks-reducer {pkg_version}",
37
38
  )
38
-
39
+
39
40
  parser.add_argument(
40
41
  "input_file",
41
42
  type=str,
@@ -113,31 +114,34 @@ def gather_input_files(paths: List[str]) -> List[str]:
113
114
  return files
114
115
 
115
116
 
117
+ def _launch_gui(argv: Sequence[str]) -> bool:
118
+ """Attempt to launch the GUI with the provided arguments."""
119
+
120
+ try:
121
+ gui_module = import_module(".gui", __package__)
122
+ except ImportError:
123
+ return False
124
+
125
+ gui_main = getattr(gui_module, "main", None)
126
+ if gui_main is None:
127
+ return False
128
+
129
+ return bool(gui_main(list(argv)))
130
+
131
+
116
132
  def main(argv: Optional[Sequence[str]] = None) -> None:
117
133
  """Entry point for the command line interface.
118
134
 
119
135
  Launch the GUI when run without arguments, otherwise defer to the CLI.
120
136
  """
121
137
 
122
- # Check if running without arguments
123
138
  if argv is None:
124
139
  argv_list = sys.argv[1:]
125
140
  else:
126
141
  argv_list = list(argv)
127
142
 
128
- # Launch GUI if no arguments provided
129
143
  if not argv_list:
130
- gui_launched = False
131
-
132
- try:
133
- from .gui import main as gui_main
134
-
135
- gui_launched = gui_main([])
136
- except ImportError:
137
- # GUI dependencies not available, show help instead
138
- gui_launched = False
139
-
140
- if gui_launched:
144
+ if _launch_gui(argv_list):
141
145
  return
142
146
 
143
147
  parser = _build_parser()
@@ -145,7 +149,7 @@ def main(argv: Optional[Sequence[str]] = None) -> None:
145
149
  return
146
150
 
147
151
  parser = _build_parser()
148
- parsed_args = parser.parse_args(argv)
152
+ parsed_args = parser.parse_args(argv_list)
149
153
  start_time = time.time()
150
154
 
151
155
  files = gather_input_files(parsed_args.input_file)
@@ -38,6 +38,7 @@ def find_ffmpeg() -> Optional[str]:
38
38
  # Try bundled ffmpeg from imageio-ffmpeg first
39
39
  try:
40
40
  import imageio_ffmpeg
41
+
41
42
  bundled_path = imageio_ffmpeg.get_ffmpeg_exe()
42
43
  if bundled_path and os.path.isfile(bundled_path):
43
44
  return bundled_path
@@ -161,11 +162,11 @@ def check_cuda_available(ffmpeg_path: Optional[str] = None) -> bool:
161
162
  try:
162
163
  ffmpeg_path = ffmpeg_path or get_ffmpeg_path()
163
164
  result = subprocess.run(
164
- [ffmpeg_path, "-encoders"],
165
- capture_output=True,
166
- text=True,
165
+ [ffmpeg_path, "-encoders"],
166
+ capture_output=True,
167
+ text=True,
167
168
  timeout=5,
168
- creationflags=creationflags
169
+ creationflags=creationflags,
169
170
  )
170
171
  except (
171
172
  subprocess.TimeoutExpired,
@@ -193,7 +194,7 @@ def run_timed_ffmpeg_command(
193
194
  process_callback: Optional[callable] = None,
194
195
  ) -> None:
195
196
  """Execute an FFmpeg command while streaming progress information.
196
-
197
+
197
198
  Args:
198
199
  process_callback: Optional callback that receives the subprocess.Popen object
199
200
  """
@@ -243,7 +244,7 @@ def run_timed_ffmpeg_command(
243
244
 
244
245
  sys.stderr.write(line)
245
246
  sys.stderr.flush()
246
-
247
+
247
248
  # Send FFmpeg output to reporter for GUI display
248
249
  progress_reporter.log(line.strip())
249
250
 
@@ -1215,10 +1215,10 @@ class TalksReducerGUI:
1215
1215
  self.drop_hint_button.grid_remove()
1216
1216
  self.open_button.grid()
1217
1217
  self.open_button.lift() # Ensure open_button is above drop_hint_button
1218
- print("success status")
1218
+ # print("success status")
1219
1219
  else:
1220
1220
  self.open_button.grid_remove()
1221
- print("not success status")
1221
+ # print("not success status")
1222
1222
  if (
1223
1223
  self.simple_mode_var.get()
1224
1224
  and not is_processing
@@ -158,7 +158,7 @@ def speed_up_video(
158
158
  )
159
159
 
160
160
  reporter.log("Extracting audio...")
161
- process_callback = getattr(reporter, 'process_callback', None)
161
+ process_callback = getattr(reporter, "process_callback", None)
162
162
  run_timed_ffmpeg_command(
163
163
  extract_command,
164
164
  reporter=reporter,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: talks-reducer
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: CLI for speeding up long-form talks by removing silence
5
5
  Author: Talks Reducer Maintainers
6
6
  License-Expression: MIT
@@ -32,6 +32,10 @@ project was renamed from **jumpcutter** to emphasize its focus on conference tal
32
32
  - 1h 19m, 751 MB — Talks Reducer
33
33
  - 1h 19m, 171 MB — Talks Reducer `--small`
34
34
 
35
+ ## Changelog
36
+
37
+ See [CHANGELOG.md](CHANGELOG.md).
38
+
35
39
  ## Install GUI (Windows, macOS)
36
40
  Go to the [releases page](https://github.com/popstas/talks-reducer/releases) and download the appropriate artifact:
37
41
 
@@ -50,9 +54,9 @@ pip install talks-reducer
50
54
  The `--small` preset applies a 720p video scale and 128 kbps audio bitrate, making it useful for sharing talks over constrained
51
55
  connections. Without `--small`, the script aims to preserve original quality while removing silence.
52
56
 
53
- > **Tip:** Running `talks-reducer-gui` without arguments opens the Tkinter interface. Passing regular CLI options (for example,
54
- > `talks-reducer-gui --small input.mp4`) now executes the command-line pipeline, so you can keep a single shortcut for both
55
- > workflows.
57
+ > **Tip:** The `talks-reducer` and `talks-reducer-gui` commands now behave the same way: launching them without arguments opens
58
+ > the Tkinter interface, while passing regular CLI options (for example, `talks-reducer --small input.mp4`) executes the
59
+ > command-line pipeline. You can keep a single shortcut for both workflows.
56
60
 
57
61
  When CUDA-capable hardware is available the pipeline leans on GPU encoders to keep export times low, but it still runs great on
58
62
  CPUs.
@@ -18,4 +18,5 @@ talks_reducer.egg-info/entry_points.txt
18
18
  talks_reducer.egg-info/requires.txt
19
19
  talks_reducer.egg-info/top_level.txt
20
20
  tests/test_audio.py
21
+ tests/test_cli.py
21
22
  tests/test_pipeline_service.py
@@ -0,0 +1,78 @@
1
+ """Tests for the CLI entry point behaviour."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from types import SimpleNamespace
7
+ from unittest import mock
8
+
9
+ import pytest
10
+
11
+ from talks_reducer import cli
12
+
13
+
14
+ def test_main_launches_gui_when_no_args(monkeypatch: pytest.MonkeyPatch) -> None:
15
+ """The GUI should be launched when no CLI arguments are provided."""
16
+
17
+ launch_calls: list[list[str]] = []
18
+
19
+ def fake_launch(argv: list[str]) -> bool:
20
+ launch_calls.append(list(argv))
21
+ return True
22
+
23
+ def fail_build_parser() -> None:
24
+ raise AssertionError("Parser should not be built when GUI launches")
25
+
26
+ monkeypatch.setattr(cli, "_launch_gui", fake_launch)
27
+ monkeypatch.setattr(cli, "_build_parser", fail_build_parser)
28
+
29
+ cli.main([])
30
+
31
+ assert launch_calls == [[]]
32
+
33
+
34
+ def test_main_runs_cli_with_arguments(monkeypatch: pytest.MonkeyPatch) -> None:
35
+ """Providing CLI arguments should bypass the GUI and run the pipeline."""
36
+
37
+ parsed_args = SimpleNamespace(
38
+ input_file=["input.mp4"],
39
+ output_file=None,
40
+ temp_folder=None,
41
+ silent_threshold=None,
42
+ silent_speed=None,
43
+ sounded_speed=None,
44
+ frame_spreadage=None,
45
+ sample_rate=None,
46
+ small=False,
47
+ )
48
+
49
+ parser_mock = mock.Mock()
50
+ parser_mock.parse_args.return_value = parsed_args
51
+
52
+ outputs: list[cli.ProcessingOptions] = []
53
+
54
+ class DummyReporter:
55
+ def log(self, _message: str) -> None: # pragma: no cover - simple stub
56
+ pass
57
+
58
+ def fake_speed_up_video(options: cli.ProcessingOptions, reporter: object):
59
+ outputs.append(options)
60
+ return SimpleNamespace(output_file=Path("/tmp/output.mp4"))
61
+
62
+ def fake_gather_input_files(_paths: list[str]) -> list[str]:
63
+ return ["/tmp/input.mp4"]
64
+
65
+ def fail_launch(_argv: list[str]) -> bool:
66
+ raise AssertionError("GUI should not be launched when arguments exist")
67
+
68
+ monkeypatch.setattr(cli, "_build_parser", lambda: parser_mock)
69
+ monkeypatch.setattr(cli, "gather_input_files", fake_gather_input_files)
70
+ monkeypatch.setattr(cli, "speed_up_video", fake_speed_up_video)
71
+ monkeypatch.setattr(cli, "TqdmProgressReporter", lambda: DummyReporter())
72
+ monkeypatch.setattr(cli, "_launch_gui", fail_launch)
73
+
74
+ cli.main(["input.mp4"])
75
+
76
+ parser_mock.parse_args.assert_called_once_with(["input.mp4"])
77
+ assert len(outputs) == 1
78
+ assert outputs[0].input_file == Path("/tmp/input.mp4")
File without changes
File without changes