better-ffmpeg-progress 5.4.1__tar.gz → 6.0.0__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 (21) hide show
  1. {better_ffmpeg_progress-5.4.1/better_ffmpeg_progress.egg-info → better_ffmpeg_progress-6.0.0}/PKG-INFO +51 -13
  2. better_ffmpeg_progress-5.4.1/PKG-INFO → better_ffmpeg_progress-6.0.0/README.md +40 -24
  3. {better_ffmpeg_progress-5.4.1 → better_ffmpeg_progress-6.0.0}/better_ffmpeg_progress/__init__.py +3 -0
  4. {better_ffmpeg_progress-5.4.1 → better_ffmpeg_progress-6.0.0}/better_ffmpeg_progress/better_ffmpeg_progress.py +67 -25
  5. {better_ffmpeg_progress-5.4.1 → better_ffmpeg_progress-6.0.0}/better_ffmpeg_progress/progress_bars.py +37 -8
  6. better_ffmpeg_progress-6.0.0/better_ffmpeg_progress/py.typed +0 -0
  7. {better_ffmpeg_progress-5.4.1 → better_ffmpeg_progress-6.0.0}/better_ffmpeg_progress/terminate_process.py +54 -37
  8. {better_ffmpeg_progress-5.4.1 → better_ffmpeg_progress-6.0.0}/better_ffmpeg_progress/utils.py +77 -21
  9. better_ffmpeg_progress-5.4.1/README.md → better_ffmpeg_progress-6.0.0/better_ffmpeg_progress.egg-info/PKG-INFO +63 -6
  10. {better_ffmpeg_progress-5.4.1 → better_ffmpeg_progress-6.0.0}/better_ffmpeg_progress.egg-info/SOURCES.txt +5 -2
  11. {better_ffmpeg_progress-5.4.1 → better_ffmpeg_progress-6.0.0}/better_ffmpeg_progress.egg-info/requires.txt +3 -1
  12. better_ffmpeg_progress-6.0.0/pyproject.toml +39 -0
  13. better_ffmpeg_progress-6.0.0/tests/test_ffmpeg_process.py +152 -0
  14. better_ffmpeg_progress-6.0.0/tests/test_utils.py +119 -0
  15. better_ffmpeg_progress-5.4.1/setup.py +0 -24
  16. {better_ffmpeg_progress-5.4.1 → better_ffmpeg_progress-6.0.0}/LICENSE +0 -0
  17. {better_ffmpeg_progress-5.4.1 → better_ffmpeg_progress-6.0.0}/better_ffmpeg_progress/enums.py +0 -0
  18. {better_ffmpeg_progress-5.4.1 → better_ffmpeg_progress-6.0.0}/better_ffmpeg_progress/exceptions.py +0 -0
  19. {better_ffmpeg_progress-5.4.1 → better_ffmpeg_progress-6.0.0}/better_ffmpeg_progress.egg-info/dependency_links.txt +0 -0
  20. {better_ffmpeg_progress-5.4.1 → better_ffmpeg_progress-6.0.0}/better_ffmpeg_progress.egg-info/top_level.txt +0 -0
  21. {better_ffmpeg_progress-5.4.1 → better_ffmpeg_progress-6.0.0}/setup.cfg +0 -0
@@ -1,21 +1,24 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: better-ffmpeg-progress
3
- Version: 5.4.1
3
+ Version: 6.0.0
4
4
  Summary: Run FFmpeg & see percentage progress + ETA.
5
- Home-page: https://github.com/CrypticSignal/better-ffmpeg-progress
6
- Author: GitHub.com/CrypticSignal
7
- Author-email: theaudiophile@outlook.com
5
+ Author-email: "GitHub.com/CrypticSignal" <theaudiophile@outlook.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/CrypticSignal/better-ffmpeg-progress
8
+ Project-URL: Changelog, https://github.com/CrypticSignal/better-ffmpeg-progress/blob/main/CHANGELOG.md
8
9
  Keywords: ffmpeg,progress
9
10
  Classifier: Programming Language :: Python :: 3
10
- Classifier: License :: OSI Approved :: MIT License
11
11
  Classifier: Operating System :: OS Independent
12
- Requires-Python: >=3.7
12
+ Classifier: Typing :: Typed
13
+ Requires-Python: >=3.9
13
14
  Description-Content-Type: text/markdown
14
15
  License-File: LICENSE
15
16
  Requires-Dist: psutil
16
- Requires-Dist: requests
17
17
  Requires-Dist: rich
18
18
  Requires-Dist: tqdm
19
+ Provides-Extra: test
20
+ Requires-Dist: pytest; extra == "test"
21
+ Dynamic: license-file
19
22
 
20
23
  <div align="center">
21
24
 
@@ -65,9 +68,8 @@ command = [
65
68
  ]
66
69
 
67
70
  try:
71
+ # Pass use_tqdm=True to use tqdm instead of Rich.
68
72
  process = FfmpegProcess(command)
69
- # Uncomment this line to use tqdm instead of Rich.
70
- # process.use_tqdm = True
71
73
 
72
74
  process.run()
73
75
  except FfmpegProcessError as e:
@@ -137,14 +139,50 @@ An instance of `FfmpegProcess` accepts the following optional arguments:
137
139
  * `ffmpeg_log_file` — Filepath or file-like object to which FFmpeg stderr will be written. Default: `<input filename>_ffmpeg_log.txt`.
138
140
  * `print_detected_duration` — Print the detected duration. Default: `False`.
139
141
  * `duration_override` — Specify input file duration in seconds to use for progress calculation, instead of trying to detect this automatically with FFprobe. Default: `None`.
142
+ * `use_tqdm` — Use tqdm instead of Rich for the progress bar. Default: `False`.
143
+ * `overwrite` — What to do if the output file already exists: `True` overwrites it, `False` raises `FfmpegCommandError`, and `None` asks whether to overwrite it. Ignored if the command contains `-y` or `-n`. Default: `None`.
140
144
 
141
145
  The `run` method accepts the following optional argument:
142
146
  * `print_command` — Print the FFmpeg command being executed. Default: `False`.
143
147
 
144
148
  ## Using tqdm
145
- By default, the [Rich](https://github.com/Textualize/rich) library is used to display a progress bar. [tqdm](https://github.com/tqdm/tqdm) can be used instead by setting `process.use_tqdm = True`:
149
+ By default, the [Rich](https://github.com/Textualize/rich) library is used to display a progress bar. [tqdm](https://github.com/tqdm/tqdm) can be used instead by passing `use_tqdm=True`:
146
150
  ```python
147
- process = FfmpegProcess(command)
148
- process.use_tqdm = True
151
+ process = FfmpegProcess(command, use_tqdm=True)
149
152
  process.run()
150
153
  ```
154
+ ## Limitations
155
+ - Progress is calculated from the duration of the first input (the first `-i`).
156
+ - The last argument of the command is treated as the output file, e.g. when checking whether it already exists. With multiple outputs, only the last one is checked.
157
+
158
+ ## Development
159
+ Install the package in editable mode with the test dependencies, then run the tests (FFmpeg and FFprobe must be installed):
160
+ ```bash
161
+ pip install -e ".[test]"
162
+ ```
163
+ ```bash
164
+ pytest
165
+ ```
166
+ `test.py` is a standalone example that encodes a test pattern and shows the progress bar.
167
+
168
+ ## Publishing
169
+ Update `__version__` in `better_ffmpeg_progress/__init__.py` and add an entry to `CHANGELOG.md`.
170
+
171
+ One-time setup:
172
+ ```bash
173
+ pip install --upgrade build twine pkginfo
174
+ ```
175
+
176
+ Then, for each release:
177
+ ```bash
178
+ rm -rf dist
179
+ ```
180
+ ```bash
181
+ python -m build
182
+ ```
183
+ ```bash
184
+ twine check dist/*
185
+ ```
186
+ ```bash
187
+ twine upload dist/*
188
+ ```
@@ -1,22 +1,3 @@
1
- Metadata-Version: 2.1
2
- Name: better-ffmpeg-progress
3
- Version: 5.4.1
4
- Summary: Run FFmpeg & see percentage progress + ETA.
5
- Home-page: https://github.com/CrypticSignal/better-ffmpeg-progress
6
- Author: GitHub.com/CrypticSignal
7
- Author-email: theaudiophile@outlook.com
8
- Keywords: ffmpeg,progress
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: License :: OSI Approved :: MIT License
11
- Classifier: Operating System :: OS Independent
12
- Requires-Python: >=3.7
13
- Description-Content-Type: text/markdown
14
- License-File: LICENSE
15
- Requires-Dist: psutil
16
- Requires-Dist: requests
17
- Requires-Dist: rich
18
- Requires-Dist: tqdm
19
-
20
1
  <div align="center">
21
2
 
22
3
  # Better FFmpeg Progress
@@ -65,9 +46,8 @@ command = [
65
46
  ]
66
47
 
67
48
  try:
49
+ # Pass use_tqdm=True to use tqdm instead of Rich.
68
50
  process = FfmpegProcess(command)
69
- # Uncomment this line to use tqdm instead of Rich.
70
- # process.use_tqdm = True
71
51
 
72
52
  process.run()
73
53
  except FfmpegProcessError as e:
@@ -137,14 +117,50 @@ An instance of `FfmpegProcess` accepts the following optional arguments:
137
117
  * `ffmpeg_log_file` — Filepath or file-like object to which FFmpeg stderr will be written. Default: `<input filename>_ffmpeg_log.txt`.
138
118
  * `print_detected_duration` — Print the detected duration. Default: `False`.
139
119
  * `duration_override` — Specify input file duration in seconds to use for progress calculation, instead of trying to detect this automatically with FFprobe. Default: `None`.
120
+ * `use_tqdm` — Use tqdm instead of Rich for the progress bar. Default: `False`.
121
+ * `overwrite` — What to do if the output file already exists: `True` overwrites it, `False` raises `FfmpegCommandError`, and `None` asks whether to overwrite it. Ignored if the command contains `-y` or `-n`. Default: `None`.
140
122
 
141
123
  The `run` method accepts the following optional argument:
142
124
  * `print_command` — Print the FFmpeg command being executed. Default: `False`.
143
125
 
144
126
  ## Using tqdm
145
- By default, the [Rich](https://github.com/Textualize/rich) library is used to display a progress bar. [tqdm](https://github.com/tqdm/tqdm) can be used instead by setting `process.use_tqdm = True`:
127
+ By default, the [Rich](https://github.com/Textualize/rich) library is used to display a progress bar. [tqdm](https://github.com/tqdm/tqdm) can be used instead by passing `use_tqdm=True`:
146
128
  ```python
147
- process = FfmpegProcess(command)
148
- process.use_tqdm = True
129
+ process = FfmpegProcess(command, use_tqdm=True)
149
130
  process.run()
150
131
  ```
132
+ ## Limitations
133
+ - Progress is calculated from the duration of the first input (the first `-i`).
134
+ - The last argument of the command is treated as the output file, e.g. when checking whether it already exists. With multiple outputs, only the last one is checked.
135
+
136
+ ## Development
137
+ Install the package in editable mode with the test dependencies, then run the tests (FFmpeg and FFprobe must be installed):
138
+ ```bash
139
+ pip install -e ".[test]"
140
+ ```
141
+ ```bash
142
+ pytest
143
+ ```
144
+ `test.py` is a standalone example that encodes a test pattern and shows the progress bar.
145
+
146
+ ## Publishing
147
+ Update `__version__` in `better_ffmpeg_progress/__init__.py` and add an entry to `CHANGELOG.md`.
148
+
149
+ One-time setup:
150
+ ```bash
151
+ pip install --upgrade build twine pkginfo
152
+ ```
153
+
154
+ Then, for each release:
155
+ ```bash
156
+ rm -rf dist
157
+ ```
158
+ ```bash
159
+ python -m build
160
+ ```
161
+ ```bash
162
+ twine check dist/*
163
+ ```
164
+ ```bash
165
+ twine upload dist/*
166
+ ```
@@ -1,3 +1,5 @@
1
+ __version__ = "6.0.0"
2
+
1
3
  from .better_ffmpeg_progress import FfmpegProcess
2
4
  from .enums import FfmpegLogLevel
3
5
  from .exceptions import (
@@ -8,6 +10,7 @@ from .exceptions import (
8
10
  )
9
11
 
10
12
  __all__ = [
13
+ "__version__",
11
14
  "FfmpegProcess",
12
15
  "FfmpegLogLevel",
13
16
  "FfmpegProcessError",
@@ -4,8 +4,7 @@ import re
4
4
  import shlex
5
5
  from pathlib import Path
6
6
  import subprocess
7
- from sys import exit
8
- from typing import List, Optional, Union
7
+ from typing import IO, Any, List, Optional, Union
9
8
 
10
9
  from .enums import FfmpegLogLevel
11
10
  from .exceptions import (
@@ -19,11 +18,19 @@ from .utils import (
19
18
  get_media_duration,
20
19
  get_expected_output_duration,
21
20
  is_lavfi_input,
21
+ is_local_file_input,
22
+ is_stdin_input,
22
23
  )
23
24
  from .terminate_process import terminate_ffmpeg_process
24
25
  from .progress_bars import use_rich, use_tqdm
25
26
 
26
27
  _FFMPEG_OVERWRITE_FLAG = "-y"
28
+ _FFMPEG_NO_OVERWRITE_FLAG = "-n"
29
+
30
+
31
+ def _safe_filename(name: str) -> str:
32
+ """Replaces characters that are not valid in filenames on every OS."""
33
+ return re.sub(r"[^\w.,-]", "_", name)
27
34
 
28
35
 
29
36
  class FfmpegProcess:
@@ -59,9 +66,11 @@ class FfmpegProcess:
59
66
  self,
60
67
  command: List[str],
61
68
  ffmpeg_log_level: Optional[Union[FfmpegLogLevel, str]] = None,
62
- ffmpeg_log_file: Optional[Union[str, os.PathLike]] = None,
69
+ ffmpeg_log_file: Optional[Union[str, os.PathLike, IO[Any]]] = None,
63
70
  print_detected_duration: bool = False,
64
71
  duration_override: Optional[float] = None,
72
+ overwrite: Optional[bool] = None,
73
+ use_tqdm: bool = False,
65
74
  ):
66
75
  """
67
76
  Create an FFmpeg process with progress reporting.
@@ -72,12 +81,19 @@ class FfmpegProcess:
72
81
  ffmpeg_log_level:
73
82
  FFmpeg log level enum or case-insensitive string.
74
83
  ffmpeg_log_file:
75
- File path or file-like object used for FFmpeg stderr.
84
+ File path or file object (with a file descriptor, e.g. one returned by open())
85
+ to which FFmpeg's stderr is written.
76
86
  print_detected_duration:
77
87
  Print the duration used for progress calculation.
78
88
  duration_override:
79
89
  Specify input file duration in seconds to use for progress calculation, instead of trying to detect this automatically with FFprobe. This is useful for partial, damaged, streamed, or
80
90
  synthetic inputs whose duration cannot be detected correctly.
91
+ overwrite:
92
+ What to do if the output file already exists. True overwrites it, False raises
93
+ FfmpegCommandError, and None (the default) asks the user. Ignored if the command
94
+ contains -y or -n.
95
+ use_tqdm:
96
+ Use tqdm instead of Rich for the progress bar.
81
97
  """
82
98
  validate_ffmpeg_command(command)
83
99
 
@@ -118,10 +134,17 @@ class FfmpegProcess:
118
134
  # A filtergraph such as "testsrc2=size=1920x1080" can contain characters that
119
135
  # are not valid in filenames (e.g. ":" on Windows), so name the log after the
120
136
  # source instead.
121
- source_name = re.sub(r"[^\w.,-]", "_", input_file_path_str.split("=", 1)[0])
137
+ source_name = _safe_filename(input_file_path_str.split("=", 1)[0])
122
138
  self._ffmpeg_log_file = Path(f"lavfi_{source_name}_ffmpeg_log.txt")
123
- else:
139
+ elif input_file_path_str == "-":
140
+ self._ffmpeg_log_file = Path("stdin_ffmpeg_log.txt")
141
+ elif is_local_file_input(command, input_file_index):
124
142
  self._ffmpeg_log_file = Path(f"{self._input_filepath.name}_ffmpeg_log.txt")
143
+ else:
144
+ # URLs and other protocols (e.g. "pipe:0") can contain characters that are not
145
+ # valid in filenames, such as ":" or "?".
146
+ name = self._input_filepath.name or input_file_path_str
147
+ self._ffmpeg_log_file = Path(f"{_safe_filename(name)}_ffmpeg_log.txt")
125
148
  elif isinstance(ffmpeg_log_file, (str, os.PathLike)):
126
149
  self._ffmpeg_log_file = Path(ffmpeg_log_file)
127
150
  else:
@@ -142,6 +165,9 @@ class FfmpegProcess:
142
165
  raise ValueError("duration_override must be a finite number greater than 0")
143
166
 
144
167
  self._duration_secs = duration_value
168
+ elif is_stdin_input(input_file_path_str):
169
+ # Running FFprobe on standard input would consume data that is meant for FFmpeg.
170
+ self._duration_secs = None
145
171
  else:
146
172
  self._duration_secs = get_media_duration(input_file_path_str)
147
173
 
@@ -178,13 +204,29 @@ class FfmpegProcess:
178
204
  ]
179
205
  self._ffmpeg_command.extend(command[1:])
180
206
 
181
- is_overwrite_in_user_command = any(arg == _FFMPEG_OVERWRITE_FLAG for arg in command[1:])
207
+ if overwrite is not None and not isinstance(overwrite, bool):
208
+ raise TypeError("overwrite must be True, False or None")
182
209
 
183
- if self._output_filepath.exists() and not is_overwrite_in_user_command:
184
- self._handle_overwrite_prompt()
210
+ user_args = command[1:]
211
+ if _FFMPEG_OVERWRITE_FLAG not in user_args and self._output_filepath.exists():
212
+ if _FFMPEG_NO_OVERWRITE_FLAG in user_args or overwrite is False:
213
+ raise FfmpegCommandError(
214
+ f"Output file {self._output_filepath} already exists and overwriting is "
215
+ "disabled."
216
+ )
217
+ elif overwrite:
218
+ self._ffmpeg_command.insert(1, _FFMPEG_OVERWRITE_FLAG)
219
+ elif is_stdin_input(input_file_path_str):
220
+ # Prompting would read from standard input, which carries FFmpeg's input.
221
+ raise FfmpegCommandError(
222
+ f"Output file {self._output_filepath} already exists. Pass overwrite=True "
223
+ "or add -y to the command to overwrite it."
224
+ )
225
+ else:
226
+ self._handle_overwrite_prompt()
185
227
 
186
228
  self._process: Optional[subprocess.Popen] = None
187
- self.use_tqdm: bool = False
229
+ self.use_tqdm: bool = use_tqdm
188
230
 
189
231
  def run(
190
232
  self,
@@ -193,12 +235,15 @@ class FfmpegProcess:
193
235
  if print_command:
194
236
  print(f"Executing: {shlex.join(self._ffmpeg_command)}")
195
237
 
196
- try:
197
- creationflags = 0
198
- # Windows
199
- if os.name == "nt":
200
- creationflags = subprocess.CREATE_NEW_PROCESS_GROUP
238
+ # Run FFmpeg in its own process group, so that terminating the group on
239
+ # KeyboardInterrupt does not also terminate this Python process.
240
+ popen_kwargs = {}
241
+ if os.name == "nt":
242
+ popen_kwargs["creationflags"] = subprocess.CREATE_NEW_PROCESS_GROUP
243
+ else:
244
+ popen_kwargs["start_new_session"] = True
201
245
 
246
+ try:
202
247
  if isinstance(self._ffmpeg_log_file, Path):
203
248
  with open(
204
249
  self._ffmpeg_log_file,
@@ -209,14 +254,14 @@ class FfmpegProcess:
209
254
  self._ffmpeg_command,
210
255
  stdout=subprocess.PIPE,
211
256
  stderr=f,
212
- creationflags=creationflags,
257
+ **popen_kwargs,
213
258
  )
214
259
  else:
215
260
  self._process = subprocess.Popen(
216
261
  self._ffmpeg_command,
217
262
  stdout=subprocess.PIPE,
218
263
  stderr=self._ffmpeg_log_file,
219
- creationflags=creationflags,
264
+ **popen_kwargs,
220
265
  )
221
266
  except Exception as e:
222
267
  raise FfmpegProcessError(f"Error starting FFmpeg process: {e}") from e
@@ -226,14 +271,11 @@ class FfmpegProcess:
226
271
  use_tqdm(self, self._process)
227
272
  else:
228
273
  use_rich(self, self._process)
229
- except KeyboardInterrupt:
230
- self._terminate()
274
+ except KeyboardInterrupt as e:
275
+ terminate_ffmpeg_process(self._process)
276
+ raise FfmpegProcessInterruptedError(
277
+ "[KeyboardInterrupt] FFmpeg process cancelled."
278
+ ) from e
231
279
  finally:
232
280
  if self._process and self._process.stdout:
233
281
  self._process.stdout.close()
234
-
235
- def _terminate(self):
236
- if self._process:
237
- terminate_ffmpeg_process(self._process)
238
- else:
239
- exit()
@@ -13,6 +13,7 @@ from rich.progress import (
13
13
  from tqdm import tqdm
14
14
 
15
15
  from .utils import (
16
+ build_failure_message,
16
17
  estimate_final_size_mb,
17
18
  parse_ffmpeg_progress_line,
18
19
  parse_ffmpeg_total_size_line,
@@ -27,8 +28,31 @@ _MAX_FILENAME_LENGTH = 60
27
28
 
28
29
 
29
30
  def _format_size(current_size_mb: float, estimated_size_mb: Optional[float]) -> str:
30
- estimate = f"~{estimated_size_mb:.2f} MB" if estimated_size_mb is not None else "~? MB"
31
- return f"({current_size_mb:.2f} MB / {estimate})"
31
+ """
32
+ Returns e.g. "(3.25 MB / ~14.33 MB)", "(3.25 MB)" while there is no estimate yet, or ""
33
+ if FFmpeg has not reported a size (e.g. at the start, or when the output is "-f null").
34
+ """
35
+ # Also hide sizes that would display as "0.00 MB", such as a container header of a few bytes.
36
+ if round(current_size_mb, 2) <= 0:
37
+ return ""
38
+ if estimated_size_mb is None:
39
+ return f"({current_size_mb:.2f} MB)"
40
+ return f"({current_size_mb:.2f} MB / ~{estimated_size_mb:.2f} MB)"
41
+
42
+
43
+ def _with_size(text: str, current_size_mb: float, estimated_size_mb: Optional[float]) -> str:
44
+ size = _format_size(current_size_mb, estimated_size_mb)
45
+ return f"{text} {size}" if size else text
46
+
47
+
48
+ def _drain(process: subprocess.Popen) -> None:
49
+ """
50
+ Reads FFmpeg's progress output until it ends. FFmpeg writes it to a pipe, so it must be read
51
+ even when it is not displayed: once the pipe's buffer is full, FFmpeg blocks and never exits.
52
+ """
53
+ if process.stdout is not None:
54
+ for _ in process.stdout:
55
+ pass
32
56
 
33
57
 
34
58
  def use_rich(ffmpeg_process_instance: "FfmpegProcess", process: subprocess.Popen) -> None:
@@ -75,11 +99,13 @@ def use_rich(ffmpeg_process_instance: "FfmpegProcess", process: subprocess.Popen
75
99
  update_progress(
76
100
  task_id,
77
101
  completed=progress_val_secs,
78
- description=f"Processing {input_filename} "
79
- f"{_format_size(current_size_mb, estimated_size_mb)}",
102
+ description=_with_size(
103
+ f"Processing {input_filename}", current_size_mb, estimated_size_mb
104
+ ),
80
105
  )
81
106
  else:
82
107
  print(f"Processing '{input_filename}'...")
108
+ _drain(process)
83
109
 
84
110
  process.wait()
85
111
 
@@ -93,13 +119,15 @@ def use_rich(ffmpeg_process_instance: "FfmpegProcess", process: subprocess.Popen
93
119
  )
94
120
  update_progress(
95
121
  task_id,
96
- description=f"✓ Processed '{input_filename}' ({current_size_mb:.2f} MB)",
122
+ description=_with_size(
123
+ f"✓ Processed '{input_filename}'", current_size_mb, None
124
+ ),
97
125
  )
98
126
  else:
99
127
  if task_id is not None:
100
128
  progress_bar.update(task_id)
101
129
 
102
- raise FfmpegProcessError(f"FFmpeg process failed. Check '{log_file}' for details.")
130
+ raise FfmpegProcessError(build_failure_message(log_file))
103
131
 
104
132
 
105
133
  def use_tqdm(ffmpeg_process_instance: "FfmpegProcess", process: subprocess.Popen) -> None:
@@ -145,6 +173,7 @@ def use_tqdm(ffmpeg_process_instance: "FfmpegProcess", process: subprocess.Popen
145
173
  progress_bar.set_postfix_str(_format_size(current_size_mb, estimated_size_mb))
146
174
  else:
147
175
  print(f"Processing '{input_filename}'...")
176
+ _drain(process)
148
177
 
149
178
  process.wait()
150
179
 
@@ -152,7 +181,7 @@ def use_tqdm(ffmpeg_process_instance: "FfmpegProcess", process: subprocess.Popen
152
181
  if progress_bar:
153
182
  progress_bar.n = duration_secs
154
183
  progress_bar.set_description(f"✓ Processed '{input_filename}'")
155
- progress_bar.set_postfix_str(f"{current_size_mb:.2f} MB")
184
+ progress_bar.set_postfix_str(_format_size(current_size_mb, None))
156
185
  progress_bar.close()
157
186
  else:
158
187
  print(f"✓ Processed '{input_filename}'")
@@ -160,4 +189,4 @@ def use_tqdm(ffmpeg_process_instance: "FfmpegProcess", process: subprocess.Popen
160
189
  if progress_bar:
161
190
  progress_bar.close()
162
191
 
163
- raise FfmpegProcessError(f"FFmpeg process failed. Check '{log_file}' for details.")
192
+ raise FfmpegProcessError(build_failure_message(log_file))