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.
- {talks_reducer-0.3.1/talks_reducer.egg-info → talks_reducer-0.3.2}/PKG-INFO +8 -4
- {talks_reducer-0.3.1 → talks_reducer-0.3.2}/README.md +7 -3
- {talks_reducer-0.3.1 → talks_reducer-0.3.2}/pyproject.toml +1 -1
- {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer/cli.py +21 -17
- {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer/ffmpeg.py +7 -6
- {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer/gui.py +2 -2
- {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer/pipeline.py +1 -1
- {talks_reducer-0.3.1 → talks_reducer-0.3.2/talks_reducer.egg-info}/PKG-INFO +8 -4
- {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer.egg-info/SOURCES.txt +1 -0
- talks_reducer-0.3.2/tests/test_cli.py +78 -0
- {talks_reducer-0.3.1 → talks_reducer-0.3.2}/LICENSE +0 -0
- {talks_reducer-0.3.1 → talks_reducer-0.3.2}/setup.cfg +0 -0
- {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer/__init__.py +0 -0
- {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer/__main__.py +0 -0
- {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer/audio.py +0 -0
- {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer/chunks.py +0 -0
- {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer/models.py +0 -0
- {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer/progress.py +0 -0
- {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer.egg-info/dependency_links.txt +0 -0
- {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer.egg-info/entry_points.txt +0 -0
- {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer.egg-info/requires.txt +0 -0
- {talks_reducer-0.3.1 → talks_reducer-0.3.2}/talks_reducer.egg-info/top_level.txt +0 -0
- {talks_reducer-0.3.1 → talks_reducer-0.3.2}/tests/test_audio.py +0 -0
- {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.
|
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:**
|
54
|
-
> `talks-reducer
|
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:**
|
29
|
-
> `talks-reducer
|
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.
|
@@ -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
|
-
|
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(
|
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,
|
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.
|
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:**
|
54
|
-
> `talks-reducer
|
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.
|
@@ -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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|