talks-reducer 0.5.0__tar.gz → 0.5.1__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 (29) hide show
  1. {talks_reducer-0.5.0/talks_reducer.egg-info → talks_reducer-0.5.1}/PKG-INFO +119 -93
  2. {talks_reducer-0.5.0 → talks_reducer-0.5.1}/README.md +27 -1
  3. {talks_reducer-0.5.0 → talks_reducer-0.5.1}/setup.cfg +4 -4
  4. {talks_reducer-0.5.0 → talks_reducer-0.5.1}/talks_reducer/__about__.py +1 -1
  5. {talks_reducer-0.5.0 → talks_reducer-0.5.1}/talks_reducer/cli.py +2 -2
  6. {talks_reducer-0.5.0 → talks_reducer-0.5.1}/talks_reducer/ffmpeg.py +6 -0
  7. {talks_reducer-0.5.0 → talks_reducer-0.5.1}/talks_reducer/gui.py +3 -3
  8. {talks_reducer-0.5.0 → talks_reducer-0.5.1}/talks_reducer/models.py +27 -2
  9. {talks_reducer-0.5.0 → talks_reducer-0.5.1}/talks_reducer/pipeline.py +1 -1
  10. {talks_reducer-0.5.0 → talks_reducer-0.5.1}/talks_reducer/server.py +1 -0
  11. talks_reducer-0.5.1/talks_reducer/service_client.py +102 -0
  12. {talks_reducer-0.5.0 → talks_reducer-0.5.1/talks_reducer.egg-info}/PKG-INFO +119 -93
  13. {talks_reducer-0.5.0 → talks_reducer-0.5.1}/talks_reducer.egg-info/SOURCES.txt +3 -1
  14. talks_reducer-0.5.1/tests/test_service_client.py +112 -0
  15. {talks_reducer-0.5.0 → talks_reducer-0.5.1}/LICENSE +0 -0
  16. {talks_reducer-0.5.0 → talks_reducer-0.5.1}/pyproject.toml +0 -0
  17. {talks_reducer-0.5.0 → talks_reducer-0.5.1}/talks_reducer/__init__.py +0 -0
  18. {talks_reducer-0.5.0 → talks_reducer-0.5.1}/talks_reducer/__main__.py +0 -0
  19. {talks_reducer-0.5.0 → talks_reducer-0.5.1}/talks_reducer/audio.py +0 -0
  20. {talks_reducer-0.5.0 → talks_reducer-0.5.1}/talks_reducer/chunks.py +0 -0
  21. {talks_reducer-0.5.0 → talks_reducer-0.5.1}/talks_reducer/progress.py +0 -0
  22. {talks_reducer-0.5.0 → talks_reducer-0.5.1}/talks_reducer.egg-info/dependency_links.txt +0 -0
  23. {talks_reducer-0.5.0 → talks_reducer-0.5.1}/talks_reducer.egg-info/entry_points.txt +0 -0
  24. {talks_reducer-0.5.0 → talks_reducer-0.5.1}/talks_reducer.egg-info/requires.txt +0 -0
  25. {talks_reducer-0.5.0 → talks_reducer-0.5.1}/talks_reducer.egg-info/top_level.txt +0 -0
  26. {talks_reducer-0.5.0 → talks_reducer-0.5.1}/tests/test_audio.py +0 -0
  27. {talks_reducer-0.5.0 → talks_reducer-0.5.1}/tests/test_cli.py +0 -0
  28. {talks_reducer-0.5.0 → talks_reducer-0.5.1}/tests/test_pipeline_service.py +0 -0
  29. {talks_reducer-0.5.0 → talks_reducer-0.5.1}/tests/test_server.py +0 -0
@@ -1,93 +1,119 @@
1
- Metadata-Version: 2.4
2
- Name: talks-reducer
3
- Version: 0.5.0
4
- Summary: CLI for speeding up long-form talks by removing silence
5
- Author: Talks Reducer Maintainers
6
- License-Expression: MIT
7
- Requires-Python: >=3.9
8
- Description-Content-Type: text/markdown
9
- License-File: LICENSE
10
- Requires-Dist: audiotsm>=0.1.2
11
- Requires-Dist: scipy>=1.10.0
12
- Requires-Dist: numpy>=1.22.0
13
- Requires-Dist: tqdm>=4.65.0
14
- Requires-Dist: tkinterdnd2>=0.3.0
15
- Requires-Dist: Pillow>=9.0.0
16
- Requires-Dist: imageio-ffmpeg>=0.4.8
17
- Requires-Dist: gradio>=4.0.0
18
- Provides-Extra: dev
19
- Requires-Dist: build>=1.0.0; extra == "dev"
20
- Requires-Dist: twine>=4.0.0; extra == "dev"
21
- Requires-Dist: pytest>=7.0.0; extra == "dev"
22
- Requires-Dist: black>=23.0.0; extra == "dev"
23
- Requires-Dist: isort>=5.12.0; extra == "dev"
24
- Requires-Dist: bump-my-version>=0.5.0; extra == "dev"
25
- Requires-Dist: pyinstaller>=6.4.0; extra == "dev"
26
- Dynamic: license-file
27
-
28
- # Talks Reducer
29
- Talks Reducer shortens long-form presentations by removing silent gaps and optionally re-encoding them to smaller files. The
30
- project was renamed from **jumpcutter** to emphasize its focus on conference talks and screencasts.
31
-
32
- ![Main demo](docs/assets/screencast-main.gif)
33
-
34
- ## Example
35
- - 1h 37m, 571 MB — Original OBS video recording
36
- - 1h 19m, 751 MB — Talks Reducer
37
- - 1h 19m, 171 MB — Talks Reducer `--small`
38
-
39
- ## Changelog
40
-
41
- See [CHANGELOG.md](CHANGELOG.md).
42
-
43
- ## Install GUI (Windows, macOS)
44
- Go to the [releases page](https://github.com/popstas/talks-reducer/releases) and download the appropriate artifact:
45
-
46
- - **Windows** — `talks-reducer-windows-0.4.0.zip`
47
- - **macOS** — `talks-reducer.app.zip` (but it doesn't work for me)
48
-
49
- When extracted on Windows the bundled `talks-reducer.exe` behaves like the
50
- `python talks_reducer/gui.py` entry point: double-clicking it launches the GUI
51
- and passing a video file path (for example via *Open with…* or drag-and-drop
52
- onto the executable) automatically queues that recording for processing.
53
-
54
- ## Install CLI (Linux, Windows, macOS)
55
- ```
56
- pip install talks-reducer
57
- ```
58
-
59
- **Note:** FFmpeg is now bundled automatically with the package, so you don't need to install it separately. You you need, don't know actually.
60
-
61
- The `--small` preset applies a 720p video scale and 128 kbps audio bitrate, making it useful for sharing talks over constrained
62
- connections. Without `--small`, the script aims to preserve original quality while removing silence.
63
-
64
- Example CLI usage:
65
-
66
- ```sh
67
- talks-reducer --small input.mp4
68
- ```
69
-
70
- ### Speech detection
71
-
72
- Talks Reducer now relies on its built-in volume thresholding to detect speech. Adjust `--silent_threshold` if you need to fine-tune when segments count as silence. Dropping the optional Silero VAD integration keeps the install lightweight and avoids pulling in PyTorch.
73
-
74
- When CUDA-capable hardware is available the pipeline leans on GPU encoders to keep export times low, but it still runs great on
75
- CPUs.
76
-
77
- ## Simple web server
78
-
79
- Prefer a lightweight browser interface? Launch the Gradio-powered simple mode with:
80
-
81
- ```sh
82
- talks-reducer server
83
- ```
84
-
85
- This opens a local web page featuring a drag-and-drop upload zone, a **Small video** checkbox that mirrors the CLI preset, a live
86
- progress indicator, and automatic previews of the processed output. Once the job completes you can inspect the resulting compression
87
- ratio and download the rendered video directly from the page.
88
-
89
- ## Contributing
90
- See `CONTRIBUTION.md` for development setup details and guidance on sharing improvements.
91
-
92
- ## License
93
- Talks Reducer is released under the MIT License. See `LICENSE` for the full text.
1
+ Metadata-Version: 2.4
2
+ Name: talks-reducer
3
+ Version: 0.5.1
4
+ Summary: CLI for speeding up long-form talks by removing silence
5
+ Author: Talks Reducer Maintainers
6
+ License-Expression: MIT
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: audiotsm>=0.1.2
11
+ Requires-Dist: scipy>=1.10.0
12
+ Requires-Dist: numpy>=1.22.0
13
+ Requires-Dist: tqdm>=4.65.0
14
+ Requires-Dist: tkinterdnd2>=0.3.0
15
+ Requires-Dist: Pillow>=9.0.0
16
+ Requires-Dist: imageio-ffmpeg>=0.4.8
17
+ Requires-Dist: gradio>=4.0.0
18
+ Provides-Extra: dev
19
+ Requires-Dist: build>=1.0.0; extra == "dev"
20
+ Requires-Dist: twine>=4.0.0; extra == "dev"
21
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
22
+ Requires-Dist: black>=23.0.0; extra == "dev"
23
+ Requires-Dist: isort>=5.12.0; extra == "dev"
24
+ Requires-Dist: bump-my-version>=0.5.0; extra == "dev"
25
+ Requires-Dist: pyinstaller>=6.4.0; extra == "dev"
26
+ Dynamic: license-file
27
+
28
+ # Talks Reducer
29
+ Talks Reducer shortens long-form presentations by removing silent gaps and optionally re-encoding them to smaller files. The
30
+ project was renamed from **jumpcutter** to emphasize its focus on conference talks and screencasts.
31
+
32
+ ![Main demo](docs/assets/screencast-main.gif)
33
+
34
+ ## Example
35
+ - 1h 37m, 571 MB — Original OBS video recording
36
+ - 1h 19m, 751 MB — Talks Reducer
37
+ - 1h 19m, 171 MB — Talks Reducer `--small`
38
+
39
+ ## Changelog
40
+
41
+ See [CHANGELOG.md](CHANGELOG.md).
42
+
43
+ ## Install GUI (Windows, macOS)
44
+ Go to the [releases page](https://github.com/popstas/talks-reducer/releases) and download the appropriate artifact:
45
+
46
+ - **Windows** — `talks-reducer-windows-0.4.0.zip`
47
+ - **macOS** — `talks-reducer.app.zip`
48
+
49
+ > **Troubleshooting:** If launching the bundle (or running `python talks_reducer/gui.py`) prints `macOS 26 (2600) or later required, have instead 16 (1600)!`, make sure you're using a Python build that ships a modern Tk. The stock [python.org 3.13.5 installer](https://www.python.org/downloads/release/python-3135/) includes Tk 8.6 and has been verified to work.
50
+
51
+ When extracted on Windows the bundled `talks-reducer.exe` behaves like the
52
+ `python talks_reducer/gui.py` entry point: double-clicking it launches the GUI
53
+ and passing a video file path (for example via *Open with…* or drag-and-drop
54
+ onto the executable) automatically queues that recording for processing.
55
+
56
+ ## Install CLI (Linux, Windows, macOS)
57
+ ```
58
+ pip install talks-reducer
59
+ ```
60
+
61
+ **Note:** FFmpeg is now bundled automatically with the package, so you don't need to install it separately. You you need, don't know actually.
62
+
63
+ The `--small` preset applies a 720p video scale and 128 kbps audio bitrate, making it useful for sharing talks over constrained
64
+ connections. Without `--small`, the script aims to preserve original quality while removing silence.
65
+
66
+ Example CLI usage:
67
+
68
+ ```sh
69
+ talks-reducer --small input.mp4
70
+ ```
71
+
72
+ ### Speech detection
73
+
74
+ Talks Reducer now relies on its built-in volume thresholding to detect speech. Adjust `--silent_threshold` if you need to fine-tune when segments count as silence. Dropping the optional Silero VAD integration keeps the install lightweight and avoids pulling in PyTorch.
75
+
76
+ When CUDA-capable hardware is available the pipeline leans on GPU encoders to keep export times low, but it still runs great on
77
+ CPUs.
78
+
79
+ ## Simple web server
80
+
81
+ Prefer a lightweight browser interface? Launch the Gradio-powered simple mode with:
82
+
83
+ ```sh
84
+ talks-reducer server
85
+ ```
86
+
87
+ This opens a local web page featuring a drag-and-drop upload zone, a **Small video** checkbox that mirrors the CLI preset, a live
88
+ progress indicator, and automatic previews of the processed output. Once the job completes you can inspect the resulting compression
89
+ ratio and download the rendered video directly from the page.
90
+
91
+ ### Uploading and retrieving a processed video
92
+
93
+ 1. Open the printed `http://localhost:<port>` address (the default port is `9005`).
94
+ 2. Drag a video onto the **Video file** drop zone or click to browse and select one from disk.
95
+ 3. (Optional) Enable **Small video** before the upload finishes to apply the 720p/128 kbps preset.
96
+ 4. Wait for the progress bar and log to report completion—the interface queues work automatically after the file arrives.
97
+ 5. Watch the processed preview in the **Processed video** player and click **Download processed file** to save the result locally.
98
+
99
+ Need to change where the server listens? Run `talks-reducer server --host 0.0.0.0 --port 7860` (or any other port) to bind to a
100
+ different address.
101
+
102
+ ### Automating uploads from the command line
103
+
104
+ Prefer to script uploads instead of using the browser UI? Start the server and use the bundled helper to submit a job and save
105
+ the processed video locally:
106
+
107
+ ```sh
108
+ python -m talks_reducer.service_client --server http://127.0.0.1:9005/ --input demo.mp4 --output output/demo_processed.mp4
109
+ ```
110
+
111
+ The helper wraps the Gradio API exposed by `server.py`, waits for processing to complete, then copies the rendered file to the
112
+ path you provide. Pass `--small` to mirror the **Small video** checkbox or `--print-log` to stream the server log after the
113
+ download finishes.
114
+
115
+ ## Contributing
116
+ See `CONTRIBUTION.md` for development setup details and guidance on sharing improvements.
117
+
118
+ ## License
119
+ Talks Reducer is released under the MIT License. See `LICENSE` for the full text.
@@ -17,7 +17,9 @@ See [CHANGELOG.md](CHANGELOG.md).
17
17
  Go to the [releases page](https://github.com/popstas/talks-reducer/releases) and download the appropriate artifact:
18
18
 
19
19
  - **Windows** — `talks-reducer-windows-0.4.0.zip`
20
- - **macOS** — `talks-reducer.app.zip` (but it doesn't work for me)
20
+ - **macOS** — `talks-reducer.app.zip`
21
+
22
+ > **Troubleshooting:** If launching the bundle (or running `python talks_reducer/gui.py`) prints `macOS 26 (2600) or later required, have instead 16 (1600)!`, make sure you're using a Python build that ships a modern Tk. The stock [python.org 3.13.5 installer](https://www.python.org/downloads/release/python-3135/) includes Tk 8.6 and has been verified to work.
21
23
 
22
24
  When extracted on Windows the bundled `talks-reducer.exe` behaves like the
23
25
  `python talks_reducer/gui.py` entry point: double-clicking it launches the GUI
@@ -59,6 +61,30 @@ This opens a local web page featuring a drag-and-drop upload zone, a **Small vid
59
61
  progress indicator, and automatic previews of the processed output. Once the job completes you can inspect the resulting compression
60
62
  ratio and download the rendered video directly from the page.
61
63
 
64
+ ### Uploading and retrieving a processed video
65
+
66
+ 1. Open the printed `http://localhost:<port>` address (the default port is `9005`).
67
+ 2. Drag a video onto the **Video file** drop zone or click to browse and select one from disk.
68
+ 3. (Optional) Enable **Small video** before the upload finishes to apply the 720p/128 kbps preset.
69
+ 4. Wait for the progress bar and log to report completion—the interface queues work automatically after the file arrives.
70
+ 5. Watch the processed preview in the **Processed video** player and click **Download processed file** to save the result locally.
71
+
72
+ Need to change where the server listens? Run `talks-reducer server --host 0.0.0.0 --port 7860` (or any other port) to bind to a
73
+ different address.
74
+
75
+ ### Automating uploads from the command line
76
+
77
+ Prefer to script uploads instead of using the browser UI? Start the server and use the bundled helper to submit a job and save
78
+ the processed video locally:
79
+
80
+ ```sh
81
+ python -m talks_reducer.service_client --server http://127.0.0.1:9005/ --input demo.mp4 --output output/demo_processed.mp4
82
+ ```
83
+
84
+ The helper wraps the Gradio API exposed by `server.py`, waits for processing to complete, then copies the rendered file to the
85
+ path you provide. Pass `--small` to mirror the **Small video** checkbox or `--print-log` to stream the server log after the
86
+ download finishes.
87
+
62
88
  ## Contributing
63
89
  See `CONTRIBUTION.md` for development setup details and guidance on sharing improvements.
64
90
 
@@ -1,4 +1,4 @@
1
- [egg_info]
2
- tag_build =
3
- tag_date = 0
4
-
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -2,4 +2,4 @@
2
2
 
3
3
  __all__ = ["__version__"]
4
4
 
5
- __version__ = "0.5.0"
5
+ __version__ = "0.5.1"
@@ -18,7 +18,7 @@ try:
18
18
  except Exception: # pragma: no cover - fallback if metadata file missing
19
19
  _about_version = ""
20
20
  from .ffmpeg import FFmpegNotFoundError
21
- from .models import ProcessingOptions
21
+ from .models import ProcessingOptions, default_temp_folder
22
22
  from .pipeline import speed_up_video
23
23
  from .progress import TqdmProgressReporter
24
24
 
@@ -55,7 +55,7 @@ def _build_parser() -> argparse.ArgumentParser:
55
55
  parser.add_argument(
56
56
  "--temp_folder",
57
57
  type=str,
58
- default="TEMP",
58
+ default=str(default_temp_folder()),
59
59
  help="The file path of the temporary working folder.",
60
60
  )
61
61
  parser.add_argument(
@@ -52,6 +52,9 @@ def find_ffmpeg() -> Optional[str]:
52
52
  "C:\\ProgramData\\chocolatey\\bin\\ffmpeg.exe",
53
53
  "C:\\Program Files\\ffmpeg\\bin\\ffmpeg.exe",
54
54
  "C:\\ffmpeg\\bin\\ffmpeg.exe",
55
+ "/usr/local/bin/ffmpeg",
56
+ "/opt/homebrew/bin/ffmpeg",
57
+ "/usr/bin/ffmpeg",
55
58
  "ffmpeg",
56
59
  ]
57
60
 
@@ -92,6 +95,9 @@ def find_ffprobe() -> Optional[str]:
92
95
  "C:\\ProgramData\\chocolatey\\bin\\ffprobe.exe",
93
96
  "C:\\Program Files\\ffmpeg\\bin\\ffprobe.exe",
94
97
  "C:\\ffmpeg\\bin\\ffprobe.exe",
98
+ "/usr/local/bin/ffprobe",
99
+ "/opt/homebrew/bin/ffprobe",
100
+ "/usr/bin/ffprobe",
95
101
  "ffprobe",
96
102
  ]
97
103
 
@@ -20,7 +20,7 @@ try:
20
20
  from .cli import gather_input_files
21
21
  from .cli import main as cli_main
22
22
  from .ffmpeg import FFmpegNotFoundError
23
- from .models import ProcessingOptions
23
+ from .models import ProcessingOptions, default_temp_folder
24
24
  from .pipeline import speed_up_video
25
25
  from .progress import ProgressHandle, SignalProgressReporter
26
26
  except ImportError: # pragma: no cover - handled at runtime
@@ -34,7 +34,7 @@ except ImportError: # pragma: no cover - handled at runtime
34
34
  from talks_reducer.cli import gather_input_files
35
35
  from talks_reducer.cli import main as cli_main
36
36
  from talks_reducer.ffmpeg import FFmpegNotFoundError
37
- from talks_reducer.models import ProcessingOptions
37
+ from talks_reducer.models import ProcessingOptions, default_temp_folder
38
38
  from talks_reducer.pipeline import speed_up_video
39
39
  from talks_reducer.progress import ProgressHandle, SignalProgressReporter
40
40
 
@@ -480,7 +480,7 @@ class TalksReducerGUI:
480
480
  self.advanced_frame, "Output file", self.output_var, row=0, browse=True
481
481
  )
482
482
 
483
- self.temp_var = self.tk.StringVar(value="TEMP")
483
+ self.temp_var = self.tk.StringVar(value=str(default_temp_folder()))
484
484
  self._add_entry(
485
485
  self.advanced_frame, "Temp folder", self.temp_var, row=1, browse=True
486
486
  )
@@ -2,11 +2,36 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from dataclasses import dataclass
5
+ import os
6
+ import sys
7
+ import tempfile
8
+ from dataclasses import dataclass, field
6
9
  from pathlib import Path
7
10
  from typing import Optional
8
11
 
9
12
 
13
+ def default_temp_folder() -> Path:
14
+ """Return an OS-appropriate default temporary workspace directory."""
15
+
16
+ if sys.platform == "darwin":
17
+ base = Path.home() / "Library" / "Application Support" / "talks-reducer"
18
+ elif sys.platform == "win32":
19
+ appdata = os.environ.get("LOCALAPPDATA") or os.environ.get("APPDATA")
20
+ base = (
21
+ Path(appdata)
22
+ if appdata
23
+ else Path.home() / "AppData" / "Local" / "talks-reducer"
24
+ )
25
+ else:
26
+ xdg_runtime = os.environ.get("XDG_RUNTIME_DIR")
27
+ if xdg_runtime:
28
+ base = Path(xdg_runtime) / "talks-reducer"
29
+ else:
30
+ base = Path(tempfile.gettempdir()) / "talks-reducer"
31
+
32
+ return base / "temp"
33
+
34
+
10
35
  @dataclass(frozen=True)
11
36
  class ProcessingOptions:
12
37
  """Configuration values controlling how the talks reducer processes media.
@@ -24,7 +49,7 @@ class ProcessingOptions:
24
49
  sounded_speed: float = 1.0
25
50
  frame_spreadage: int = 2
26
51
  audio_fade_envelope_size: int = 400
27
- temp_folder: Path = Path("TEMP")
52
+ temp_folder: Path = field(default_factory=default_temp_folder)
28
53
  small: bool = False
29
54
 
30
55
 
@@ -46,7 +46,7 @@ def _input_to_output_filename(filename: Path, small: bool = False) -> Path:
46
46
 
47
47
  def _create_path(path: Path) -> None:
48
48
  try:
49
- path.mkdir()
49
+ path.mkdir(parents=True, exist_ok=True)
50
50
  except OSError as exc: # pragma: no cover - defensive logging
51
51
  raise AssertionError(
52
52
  "Creation of the directory failed. (The TEMP folder may already exist. Delete or rename it, and try again.)"
@@ -296,6 +296,7 @@ def build_interface() -> gr.Blocks:
296
296
  inputs=[file_input, small_checkbox],
297
297
  outputs=[video_output, log_output, summary_output, download_output],
298
298
  queue=True,
299
+ api_name="process_video",
299
300
  )
300
301
 
301
302
  demo.queue(default_concurrency_limit=1)
@@ -0,0 +1,102 @@
1
+ """Command-line helper for sending videos to the Talks Reducer server."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import shutil
7
+ from pathlib import Path
8
+ from typing import Optional, Sequence, Tuple
9
+
10
+ from gradio_client import Client
11
+ from gradio_client import file as gradio_file
12
+
13
+
14
+ def send_video(
15
+ input_path: Path,
16
+ output_path: Optional[Path],
17
+ server_url: str,
18
+ small: bool = False,
19
+ ) -> Tuple[Path, str, str]:
20
+ """Upload *input_path* to the Gradio server and download the processed video."""
21
+
22
+ if not input_path.exists():
23
+ raise FileNotFoundError(f"Input file does not exist: {input_path}")
24
+
25
+ client = Client(server_url)
26
+ prediction = client.predict(
27
+ gradio_file(str(input_path)),
28
+ bool(small),
29
+ api_name="/process_video",
30
+ )
31
+
32
+ try:
33
+ _, log_text, summary, download_path = prediction
34
+ except (TypeError, ValueError) as exc: # pragma: no cover - defensive
35
+ raise RuntimeError("Unexpected response from server") from exc
36
+
37
+ if not download_path:
38
+ raise RuntimeError("Server did not return a processed file")
39
+
40
+ download_source = Path(str(download_path))
41
+ if output_path is None:
42
+ destination = Path.cwd() / download_source.name
43
+ else:
44
+ destination = output_path
45
+ if destination.is_dir():
46
+ destination = destination / download_source.name
47
+
48
+ destination.parent.mkdir(parents=True, exist_ok=True)
49
+ if download_source.resolve() != destination.resolve():
50
+ shutil.copy2(download_source, destination)
51
+
52
+ return destination, summary, log_text
53
+
54
+
55
+ def _build_parser() -> argparse.ArgumentParser:
56
+ parser = argparse.ArgumentParser(
57
+ description="Send a video to a running talks-reducer server and download the result.",
58
+ )
59
+ parser.add_argument("input", type=Path, help="Path to the video file to upload.")
60
+ parser.add_argument(
61
+ "--server",
62
+ default="http://127.0.0.1:9005/",
63
+ help="Base URL for the talks-reducer server (default: http://127.0.0.1:9005/).",
64
+ )
65
+ parser.add_argument(
66
+ "--output",
67
+ type=Path,
68
+ default=None,
69
+ help="Where to store the processed video. Defaults to the working directory.",
70
+ )
71
+ parser.add_argument(
72
+ "--small",
73
+ action="store_true",
74
+ help="Toggle the 'Small video' preset before processing.",
75
+ )
76
+ parser.add_argument(
77
+ "--print-log",
78
+ action="store_true",
79
+ help="Print the server log after processing completes.",
80
+ )
81
+ return parser
82
+
83
+
84
+ def main(argv: Optional[Sequence[str]] = None) -> None:
85
+ parser = _build_parser()
86
+ args = parser.parse_args(argv)
87
+
88
+ destination, summary, log_text = send_video(
89
+ input_path=args.input.expanduser(),
90
+ output_path=args.output.expanduser() if args.output else None,
91
+ server_url=args.server,
92
+ small=args.small,
93
+ )
94
+
95
+ print(summary)
96
+ print(f"Saved processed video to {destination}")
97
+ if args.print_log:
98
+ print("\nServer log:\n" + log_text)
99
+
100
+
101
+ if __name__ == "__main__": # pragma: no cover
102
+ main()
@@ -1,93 +1,119 @@
1
- Metadata-Version: 2.4
2
- Name: talks-reducer
3
- Version: 0.5.0
4
- Summary: CLI for speeding up long-form talks by removing silence
5
- Author: Talks Reducer Maintainers
6
- License-Expression: MIT
7
- Requires-Python: >=3.9
8
- Description-Content-Type: text/markdown
9
- License-File: LICENSE
10
- Requires-Dist: audiotsm>=0.1.2
11
- Requires-Dist: scipy>=1.10.0
12
- Requires-Dist: numpy>=1.22.0
13
- Requires-Dist: tqdm>=4.65.0
14
- Requires-Dist: tkinterdnd2>=0.3.0
15
- Requires-Dist: Pillow>=9.0.0
16
- Requires-Dist: imageio-ffmpeg>=0.4.8
17
- Requires-Dist: gradio>=4.0.0
18
- Provides-Extra: dev
19
- Requires-Dist: build>=1.0.0; extra == "dev"
20
- Requires-Dist: twine>=4.0.0; extra == "dev"
21
- Requires-Dist: pytest>=7.0.0; extra == "dev"
22
- Requires-Dist: black>=23.0.0; extra == "dev"
23
- Requires-Dist: isort>=5.12.0; extra == "dev"
24
- Requires-Dist: bump-my-version>=0.5.0; extra == "dev"
25
- Requires-Dist: pyinstaller>=6.4.0; extra == "dev"
26
- Dynamic: license-file
27
-
28
- # Talks Reducer
29
- Talks Reducer shortens long-form presentations by removing silent gaps and optionally re-encoding them to smaller files. The
30
- project was renamed from **jumpcutter** to emphasize its focus on conference talks and screencasts.
31
-
32
- ![Main demo](docs/assets/screencast-main.gif)
33
-
34
- ## Example
35
- - 1h 37m, 571 MB — Original OBS video recording
36
- - 1h 19m, 751 MB — Talks Reducer
37
- - 1h 19m, 171 MB — Talks Reducer `--small`
38
-
39
- ## Changelog
40
-
41
- See [CHANGELOG.md](CHANGELOG.md).
42
-
43
- ## Install GUI (Windows, macOS)
44
- Go to the [releases page](https://github.com/popstas/talks-reducer/releases) and download the appropriate artifact:
45
-
46
- - **Windows** — `talks-reducer-windows-0.4.0.zip`
47
- - **macOS** — `talks-reducer.app.zip` (but it doesn't work for me)
48
-
49
- When extracted on Windows the bundled `talks-reducer.exe` behaves like the
50
- `python talks_reducer/gui.py` entry point: double-clicking it launches the GUI
51
- and passing a video file path (for example via *Open with…* or drag-and-drop
52
- onto the executable) automatically queues that recording for processing.
53
-
54
- ## Install CLI (Linux, Windows, macOS)
55
- ```
56
- pip install talks-reducer
57
- ```
58
-
59
- **Note:** FFmpeg is now bundled automatically with the package, so you don't need to install it separately. You you need, don't know actually.
60
-
61
- The `--small` preset applies a 720p video scale and 128 kbps audio bitrate, making it useful for sharing talks over constrained
62
- connections. Without `--small`, the script aims to preserve original quality while removing silence.
63
-
64
- Example CLI usage:
65
-
66
- ```sh
67
- talks-reducer --small input.mp4
68
- ```
69
-
70
- ### Speech detection
71
-
72
- Talks Reducer now relies on its built-in volume thresholding to detect speech. Adjust `--silent_threshold` if you need to fine-tune when segments count as silence. Dropping the optional Silero VAD integration keeps the install lightweight and avoids pulling in PyTorch.
73
-
74
- When CUDA-capable hardware is available the pipeline leans on GPU encoders to keep export times low, but it still runs great on
75
- CPUs.
76
-
77
- ## Simple web server
78
-
79
- Prefer a lightweight browser interface? Launch the Gradio-powered simple mode with:
80
-
81
- ```sh
82
- talks-reducer server
83
- ```
84
-
85
- This opens a local web page featuring a drag-and-drop upload zone, a **Small video** checkbox that mirrors the CLI preset, a live
86
- progress indicator, and automatic previews of the processed output. Once the job completes you can inspect the resulting compression
87
- ratio and download the rendered video directly from the page.
88
-
89
- ## Contributing
90
- See `CONTRIBUTION.md` for development setup details and guidance on sharing improvements.
91
-
92
- ## License
93
- Talks Reducer is released under the MIT License. See `LICENSE` for the full text.
1
+ Metadata-Version: 2.4
2
+ Name: talks-reducer
3
+ Version: 0.5.1
4
+ Summary: CLI for speeding up long-form talks by removing silence
5
+ Author: Talks Reducer Maintainers
6
+ License-Expression: MIT
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: audiotsm>=0.1.2
11
+ Requires-Dist: scipy>=1.10.0
12
+ Requires-Dist: numpy>=1.22.0
13
+ Requires-Dist: tqdm>=4.65.0
14
+ Requires-Dist: tkinterdnd2>=0.3.0
15
+ Requires-Dist: Pillow>=9.0.0
16
+ Requires-Dist: imageio-ffmpeg>=0.4.8
17
+ Requires-Dist: gradio>=4.0.0
18
+ Provides-Extra: dev
19
+ Requires-Dist: build>=1.0.0; extra == "dev"
20
+ Requires-Dist: twine>=4.0.0; extra == "dev"
21
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
22
+ Requires-Dist: black>=23.0.0; extra == "dev"
23
+ Requires-Dist: isort>=5.12.0; extra == "dev"
24
+ Requires-Dist: bump-my-version>=0.5.0; extra == "dev"
25
+ Requires-Dist: pyinstaller>=6.4.0; extra == "dev"
26
+ Dynamic: license-file
27
+
28
+ # Talks Reducer
29
+ Talks Reducer shortens long-form presentations by removing silent gaps and optionally re-encoding them to smaller files. The
30
+ project was renamed from **jumpcutter** to emphasize its focus on conference talks and screencasts.
31
+
32
+ ![Main demo](docs/assets/screencast-main.gif)
33
+
34
+ ## Example
35
+ - 1h 37m, 571 MB — Original OBS video recording
36
+ - 1h 19m, 751 MB — Talks Reducer
37
+ - 1h 19m, 171 MB — Talks Reducer `--small`
38
+
39
+ ## Changelog
40
+
41
+ See [CHANGELOG.md](CHANGELOG.md).
42
+
43
+ ## Install GUI (Windows, macOS)
44
+ Go to the [releases page](https://github.com/popstas/talks-reducer/releases) and download the appropriate artifact:
45
+
46
+ - **Windows** — `talks-reducer-windows-0.4.0.zip`
47
+ - **macOS** — `talks-reducer.app.zip`
48
+
49
+ > **Troubleshooting:** If launching the bundle (or running `python talks_reducer/gui.py`) prints `macOS 26 (2600) or later required, have instead 16 (1600)!`, make sure you're using a Python build that ships a modern Tk. The stock [python.org 3.13.5 installer](https://www.python.org/downloads/release/python-3135/) includes Tk 8.6 and has been verified to work.
50
+
51
+ When extracted on Windows the bundled `talks-reducer.exe` behaves like the
52
+ `python talks_reducer/gui.py` entry point: double-clicking it launches the GUI
53
+ and passing a video file path (for example via *Open with…* or drag-and-drop
54
+ onto the executable) automatically queues that recording for processing.
55
+
56
+ ## Install CLI (Linux, Windows, macOS)
57
+ ```
58
+ pip install talks-reducer
59
+ ```
60
+
61
+ **Note:** FFmpeg is now bundled automatically with the package, so you don't need to install it separately. You you need, don't know actually.
62
+
63
+ The `--small` preset applies a 720p video scale and 128 kbps audio bitrate, making it useful for sharing talks over constrained
64
+ connections. Without `--small`, the script aims to preserve original quality while removing silence.
65
+
66
+ Example CLI usage:
67
+
68
+ ```sh
69
+ talks-reducer --small input.mp4
70
+ ```
71
+
72
+ ### Speech detection
73
+
74
+ Talks Reducer now relies on its built-in volume thresholding to detect speech. Adjust `--silent_threshold` if you need to fine-tune when segments count as silence. Dropping the optional Silero VAD integration keeps the install lightweight and avoids pulling in PyTorch.
75
+
76
+ When CUDA-capable hardware is available the pipeline leans on GPU encoders to keep export times low, but it still runs great on
77
+ CPUs.
78
+
79
+ ## Simple web server
80
+
81
+ Prefer a lightweight browser interface? Launch the Gradio-powered simple mode with:
82
+
83
+ ```sh
84
+ talks-reducer server
85
+ ```
86
+
87
+ This opens a local web page featuring a drag-and-drop upload zone, a **Small video** checkbox that mirrors the CLI preset, a live
88
+ progress indicator, and automatic previews of the processed output. Once the job completes you can inspect the resulting compression
89
+ ratio and download the rendered video directly from the page.
90
+
91
+ ### Uploading and retrieving a processed video
92
+
93
+ 1. Open the printed `http://localhost:<port>` address (the default port is `9005`).
94
+ 2. Drag a video onto the **Video file** drop zone or click to browse and select one from disk.
95
+ 3. (Optional) Enable **Small video** before the upload finishes to apply the 720p/128 kbps preset.
96
+ 4. Wait for the progress bar and log to report completion—the interface queues work automatically after the file arrives.
97
+ 5. Watch the processed preview in the **Processed video** player and click **Download processed file** to save the result locally.
98
+
99
+ Need to change where the server listens? Run `talks-reducer server --host 0.0.0.0 --port 7860` (or any other port) to bind to a
100
+ different address.
101
+
102
+ ### Automating uploads from the command line
103
+
104
+ Prefer to script uploads instead of using the browser UI? Start the server and use the bundled helper to submit a job and save
105
+ the processed video locally:
106
+
107
+ ```sh
108
+ python -m talks_reducer.service_client --server http://127.0.0.1:9005/ --input demo.mp4 --output output/demo_processed.mp4
109
+ ```
110
+
111
+ The helper wraps the Gradio API exposed by `server.py`, waits for processing to complete, then copies the rendered file to the
112
+ path you provide. Pass `--small` to mirror the **Small video** checkbox or `--print-log` to stream the server log after the
113
+ download finishes.
114
+
115
+ ## Contributing
116
+ See `CONTRIBUTION.md` for development setup details and guidance on sharing improvements.
117
+
118
+ ## License
119
+ Talks Reducer is released under the MIT License. See `LICENSE` for the full text.
@@ -13,6 +13,7 @@ talks_reducer/models.py
13
13
  talks_reducer/pipeline.py
14
14
  talks_reducer/progress.py
15
15
  talks_reducer/server.py
16
+ talks_reducer/service_client.py
16
17
  talks_reducer.egg-info/PKG-INFO
17
18
  talks_reducer.egg-info/SOURCES.txt
18
19
  talks_reducer.egg-info/dependency_links.txt
@@ -22,4 +23,5 @@ talks_reducer.egg-info/top_level.txt
22
23
  tests/test_audio.py
23
24
  tests/test_cli.py
24
25
  tests/test_pipeline_service.py
25
- tests/test_server.py
26
+ tests/test_server.py
27
+ tests/test_service_client.py
@@ -0,0 +1,112 @@
1
+ from types import SimpleNamespace
2
+
3
+ import pytest
4
+
5
+ from talks_reducer import service_client
6
+
7
+
8
+ class DummyClient:
9
+ def __init__(self, server_url: str) -> None:
10
+ self.server_url = server_url
11
+ self.predictions = []
12
+
13
+ def predict(self, *args, **kwargs): # pragma: no cover - replaced in tests
14
+ raise NotImplementedError
15
+
16
+
17
+ def test_send_video_downloads_file(monkeypatch, tmp_path):
18
+ input_file = tmp_path / "input.mp4"
19
+ input_file.write_bytes(b"input")
20
+ server_file = tmp_path / "server_output.mp4"
21
+ server_file.write_bytes(b"processed")
22
+
23
+ client_instance = DummyClient("http://localhost:9005/")
24
+
25
+ def fake_predict(file_arg, small_flag, api_name):
26
+ assert api_name == "/process_video"
27
+ assert small_flag is True
28
+ client_instance.predictions.append((file_arg, small_flag, api_name))
29
+ return (None, "log", "summary", str(server_file))
30
+
31
+ client_instance.predict = fake_predict
32
+
33
+ monkeypatch.setattr(service_client, "Client", lambda url: client_instance)
34
+ monkeypatch.setattr(
35
+ service_client, "gradio_file", lambda path: SimpleNamespace(path=path)
36
+ )
37
+
38
+ destination, summary, log_text = service_client.send_video(
39
+ input_path=input_file,
40
+ output_path=tmp_path / "output.mp4",
41
+ server_url="http://localhost:9005/",
42
+ small=True,
43
+ )
44
+
45
+ assert destination == tmp_path / "output.mp4"
46
+ assert destination.read_bytes() == server_file.read_bytes()
47
+ assert summary == "summary"
48
+ assert log_text == "log"
49
+ assert client_instance.predictions, "predict was not called"
50
+
51
+
52
+ def test_send_video_defaults_to_current_directory(monkeypatch, tmp_path, cwd_tmp_path):
53
+ input_file = tmp_path / "input.mp4"
54
+ input_file.write_bytes(b"input")
55
+ server_file = tmp_path / "server_output.mp4"
56
+ server_file.write_bytes(b"processed")
57
+
58
+ client_instance = DummyClient("http://localhost:9005/")
59
+ client_instance.predict = lambda *_, **__: (
60
+ None,
61
+ "log",
62
+ "summary",
63
+ str(server_file),
64
+ )
65
+
66
+ monkeypatch.setattr(service_client, "Client", lambda url: client_instance)
67
+ monkeypatch.setattr(
68
+ service_client, "gradio_file", lambda path: SimpleNamespace(path=path)
69
+ )
70
+
71
+ destination, _, _ = service_client.send_video(
72
+ input_path=input_file,
73
+ output_path=None,
74
+ server_url="http://localhost:9005/",
75
+ )
76
+
77
+ assert destination.parent == cwd_tmp_path
78
+ assert destination.name == server_file.name
79
+ assert destination.read_bytes() == server_file.read_bytes()
80
+
81
+
82
+ def test_main_prints_summary(monkeypatch, tmp_path, capsys):
83
+ input_file = tmp_path / "input.mp4"
84
+ destination_file = tmp_path / "output.mp4"
85
+
86
+ def fake_send_video(**kwargs):
87
+ assert kwargs["small"] is False
88
+ return destination_file, "summary", "log"
89
+
90
+ monkeypatch.setattr(
91
+ service_client, "send_video", lambda **kwargs: fake_send_video(**kwargs)
92
+ )
93
+
94
+ service_client.main(
95
+ [
96
+ str(input_file),
97
+ "--server",
98
+ "http://localhost:9005/",
99
+ "--output",
100
+ str(destination_file),
101
+ ]
102
+ )
103
+
104
+ captured = capsys.readouterr()
105
+ assert "summary" in captured.out
106
+ assert str(destination_file) in captured.out
107
+
108
+
109
+ @pytest.fixture
110
+ def cwd_tmp_path(monkeypatch, tmp_path):
111
+ monkeypatch.chdir(tmp_path)
112
+ return tmp_path
File without changes