better-ffmpeg-progress 3.1.0__tar.gz → 3.2.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: better-ffmpeg-progress
3
- Version: 3.1.0
3
+ Version: 3.2.1
4
4
  Summary: Run FFmpeg & see percentage progress + ETA.
5
5
  Home-page: https://github.com/CrypticSignal/better-ffmpeg-progress
6
6
  Author: GitHub.com/CrypticSignal
@@ -15,6 +15,7 @@ License-File: LICENSE
15
15
  Requires-Dist: ffmpeg-python
16
16
  Requires-Dist: psutil
17
17
  Requires-Dist: rich
18
+ Requires-Dist: tqdm
18
19
 
19
20
  <div align="center">
20
21
 
@@ -25,6 +26,8 @@ Requires-Dist: rich
25
26
 
26
27
  # Better FFmpeg Progress
27
28
  Runs an FFmpeg command and shows a progress bar with percentage progress, time elapsed and ETA.
29
+
30
+ The [Rich](https://github.com/Textualize/rich) library is used for the progress bar by default, [tqdm](https://github.com/tqdm/tqdm) will be used if you pass `use_tqdm=True` to the `run` method.
28
31
  </div>
29
32
 
30
33
  FFmpeg outputs something like:
@@ -38,7 +41,7 @@ Better FFmpeg Progress outputs something like:
38
41
  Where:
39
42
  - `Processing abc.webm` is the description of the progresss bar.
40
43
  - `23%` is the percentage progress.
41
- - `0:00:04` is the time (H:MM:SS) elapsed.
44
+ - `0:00:04` is the time elapsed.
42
45
  - `00:15` is the estimated time until the FFmpeg process completes.
43
46
 
44
47
  As you can see, the output of Better FFmpeg Progress is much more useful.
@@ -56,24 +59,17 @@ Here's a simple example:
56
59
  from better_ffmpeg_progress import FfmpegProcess
57
60
 
58
61
  process = FfmpegProcess(["ffmpeg", "-i", "input.mp4", "-c:v", "libx265", "output.mp4"])
59
-
60
- # return_code will be 0 if the process was successful
62
+ # return_code will be 0 if the process completed successfully, otherwise it will be 1
61
63
  return_code = process.run()
62
-
63
- if return_code == 0:
64
- # Code to run if the process was successful
65
- pass
66
- else:
67
- # Code to run if the process was unsuccessful
68
- pass
69
64
  ```
70
65
  ## Optional Arguments
71
66
  An instance of the `FfmpegProcess` class takes the following **optional** arguments:
72
67
 
73
68
  - `ffmpeg_log_level` - Desired FFmpeg log level. Default: `"verbose"`
74
- - `ffmpeg_log_file` - The filepath to save the FFmpeg log to.
75
- - `print_detected_duration` - Print the detected duration of the input file. Default: `True`
69
+ - `ffmpeg_log_file` - The filepath to save the FFmpeg log to. Default: `<input filename>_log.txt`
70
+ - `print_detected_duration` - Print the detected duration of the input file. Default: `False`
76
71
  - `print_stderr_new_line` - If better progress information cannot be shown, print FFmpeg stderr in a new line instead of replacing the current line in the terminal. Default: `False`
77
72
 
78
- The `run` method takes the following **optional** argument:
73
+ The `run` method takes the following **optional** arguments:
79
74
  - `print_command` - Print the FFmpeg command being executed. Default: `False`
75
+ - `use_tqdm` - Use [tqdm](https://github.com/tqdm/tqdm) instead of [Rich](https://github.com/Textualize/rich) for the progress bar. Default: `False`
@@ -7,6 +7,8 @@
7
7
 
8
8
  # Better FFmpeg Progress
9
9
  Runs an FFmpeg command and shows a progress bar with percentage progress, time elapsed and ETA.
10
+
11
+ The [Rich](https://github.com/Textualize/rich) library is used for the progress bar by default, [tqdm](https://github.com/tqdm/tqdm) will be used if you pass `use_tqdm=True` to the `run` method.
10
12
  </div>
11
13
 
12
14
  FFmpeg outputs something like:
@@ -20,7 +22,7 @@ Better FFmpeg Progress outputs something like:
20
22
  Where:
21
23
  - `Processing abc.webm` is the description of the progresss bar.
22
24
  - `23%` is the percentage progress.
23
- - `0:00:04` is the time (H:MM:SS) elapsed.
25
+ - `0:00:04` is the time elapsed.
24
26
  - `00:15` is the estimated time until the FFmpeg process completes.
25
27
 
26
28
  As you can see, the output of Better FFmpeg Progress is much more useful.
@@ -38,24 +40,17 @@ Here's a simple example:
38
40
  from better_ffmpeg_progress import FfmpegProcess
39
41
 
40
42
  process = FfmpegProcess(["ffmpeg", "-i", "input.mp4", "-c:v", "libx265", "output.mp4"])
41
-
42
- # return_code will be 0 if the process was successful
43
+ # return_code will be 0 if the process completed successfully, otherwise it will be 1
43
44
  return_code = process.run()
44
-
45
- if return_code == 0:
46
- # Code to run if the process was successful
47
- pass
48
- else:
49
- # Code to run if the process was unsuccessful
50
- pass
51
45
  ```
52
46
  ## Optional Arguments
53
47
  An instance of the `FfmpegProcess` class takes the following **optional** arguments:
54
48
 
55
49
  - `ffmpeg_log_level` - Desired FFmpeg log level. Default: `"verbose"`
56
- - `ffmpeg_log_file` - The filepath to save the FFmpeg log to.
57
- - `print_detected_duration` - Print the detected duration of the input file. Default: `True`
50
+ - `ffmpeg_log_file` - The filepath to save the FFmpeg log to. Default: `<input filename>_log.txt`
51
+ - `print_detected_duration` - Print the detected duration of the input file. Default: `False`
58
52
  - `print_stderr_new_line` - If better progress information cannot be shown, print FFmpeg stderr in a new line instead of replacing the current line in the terminal. Default: `False`
59
53
 
60
- The `run` method takes the following **optional** argument:
61
- - `print_command` - Print the FFmpeg command being executed. Default: `False`
54
+ The `run` method takes the following **optional** arguments:
55
+ - `print_command` - Print the FFmpeg command being executed. Default: `False`
56
+ - `use_tqdm` - Use [tqdm](https://github.com/tqdm/tqdm) instead of [Rich](https://github.com/Textualize/rich) for the progress bar. Default: `False`
@@ -2,10 +2,11 @@ from enum import Enum
2
2
  import os
3
3
  from pathlib import Path
4
4
  import psutil
5
- from queue import Empty, Queue
5
+ from queue import Queue
6
6
  import shutil
7
7
  import subprocess
8
8
  from threading import Thread
9
+ from time import sleep
9
10
  from typing import List, Optional, Union
10
11
 
11
12
  from ffmpeg import probe
@@ -18,6 +19,7 @@ from rich.progress import (
18
19
  TimeElapsedColumn,
19
20
  SpinnerColumn,
20
21
  )
22
+ from tqdm import tqdm
21
23
 
22
24
 
23
25
  class FfmpegLogLevel(Enum):
@@ -33,8 +35,6 @@ class FfmpegLogLevel(Enum):
33
35
 
34
36
 
35
37
  class FfmpegProcess:
36
- TIME_MS_PREFIX = "out_time_ms="
37
-
38
38
  @staticmethod
39
39
  def _read_pipe(pipe: subprocess.PIPE, queue: Queue, stdout: bool = False) -> None:
40
40
  """Read from pipe and put lines into queue."""
@@ -119,7 +119,7 @@ class FfmpegProcess:
119
119
  probe_data = probe(self._input_filepath)
120
120
  self._duration_secs = float(probe_data["format"]["duration"])
121
121
  if self._print_detected_duration:
122
- print(f"Detected duration: {self._duration_secs:.2f} seconds")
122
+ print(f"Detected duration: {self._duration_secs} seconds")
123
123
  except Exception:
124
124
  self._duration_secs = None
125
125
 
@@ -130,8 +130,6 @@ class FfmpegProcess:
130
130
  self._ffmpeg_log_level,
131
131
  "-progress",
132
132
  "pipe:1",
133
- "-stats_period",
134
- "0.1",
135
133
  "-nostats",
136
134
  *command[1:],
137
135
  ]
@@ -149,7 +147,119 @@ class FfmpegProcess:
149
147
  print(f"Error writing to log file: {e}")
150
148
  self.return_code = 1
151
149
 
152
- def run(self, print_command: bool = False) -> int:
150
+ def _use_rich(self, process, stdout_queue, stderr_queue) -> int:
151
+ with Progress(
152
+ SpinnerColumn(),
153
+ TextColumn("[progress.description]{task.description}"),
154
+ BarColumn(),
155
+ TaskProgressColumn(),
156
+ TimeElapsedColumn(),
157
+ TimeRemainingColumn(compact=True),
158
+ refresh_per_second=10,
159
+ ) as progress_bar:
160
+ task_id = None
161
+
162
+ if self._duration_secs:
163
+ task_id = progress_bar.add_task(
164
+ f"Processing {self._input_filepath.name}",
165
+ total=self._duration_secs,
166
+ )
167
+
168
+ while process.poll() is None:
169
+ # FFmpeg only updates encoding progress/statistics every 0.5 seconds
170
+ sleep(0.5)
171
+ # stdout
172
+ if task_id is not None and not stdout_queue.empty():
173
+ line = stdout_queue.get()
174
+ try:
175
+ value = int(line.split("=")[1]) / 1_000_000
176
+ if value <= self._duration_secs:
177
+ progress_bar.update(task_id, completed=value)
178
+ except ValueError:
179
+ pass
180
+ # stderr
181
+ if not stderr_queue.empty():
182
+ self._process_stderr(stderr_queue.get())
183
+
184
+ if process.returncode == 0 and task_id is not None:
185
+ progress_bar.update(task_id, completed=self._duration_secs)
186
+ progress_bar.columns = (
187
+ TextColumn("[progress.description]{task.description}"),
188
+ BarColumn(),
189
+ TaskProgressColumn(),
190
+ )
191
+ progress_bar.update(
192
+ task_id,
193
+ description=f"✓ Processed {self._input_filepath.name}",
194
+ )
195
+
196
+ # Process remaining stderr
197
+ while not stderr_queue.empty():
198
+ self._process_stderr(stderr_queue.get())
199
+
200
+ if process.returncode != 0:
201
+ progress_bar.update(
202
+ task_id,
203
+ description=f"The FFmpeg process did not complete successfully. Check out {self._ffmpeg_log_file} for details.",
204
+ )
205
+ return 1
206
+
207
+ return 0
208
+
209
+ def _use_tqdm(self, process, stdout_queue, stderr_queue) -> int:
210
+ pbar = None
211
+ width, _ = os.get_terminal_size()
212
+
213
+ if self._duration_secs:
214
+ pbar = tqdm(
215
+ total=self._duration_secs,
216
+ desc=f"Processing {self._input_filepath.name}",
217
+ ncols=80,
218
+ dynamic_ncols=True if width < 80 else False,
219
+ bar_format="{desc} {bar} {percentage:.1f}% {elapsed} {remaining}",
220
+ )
221
+
222
+ while process.poll() is None:
223
+ # FFmpeg only updates encoding progress/statistics every 0.5 seconds
224
+ sleep(0.5)
225
+ # stdout
226
+ if pbar and not stdout_queue.empty():
227
+ line = stdout_queue.get()
228
+ try:
229
+ value = int(line.split("=")[1]) / 1_000_000
230
+ if value <= self._duration_secs:
231
+ pbar.n = value
232
+ pbar.refresh()
233
+ except ValueError:
234
+ pass
235
+ # stderr
236
+ if not stderr_queue.empty():
237
+ self._process_stderr(stderr_queue.get())
238
+
239
+ if process.returncode == 0 and pbar:
240
+ pbar.n = self._duration_secs
241
+ pbar.set_description(f"✓ Processed {self._input_filepath.name}")
242
+
243
+ if pbar:
244
+ pbar.close()
245
+
246
+ # Process remaining stderr
247
+ while not stderr_queue.empty():
248
+ self._process_stderr(stderr_queue.get())
249
+
250
+ if process.returncode != 0:
251
+ print(
252
+ f"The FFmpeg process did not complete successfully. Check out {self._ffmpeg_log_file} for details."
253
+ )
254
+ return 1
255
+
256
+ return 0
257
+
258
+ def run(
259
+ self,
260
+ print_command: bool = False,
261
+ use_tqdm: bool = False,
262
+ ) -> int:
153
263
  if hasattr(self, "return_code") and self.return_code != 0:
154
264
  return 1
155
265
 
@@ -206,72 +316,13 @@ class FfmpegProcess:
206
316
  ).start()
207
317
 
208
318
  try:
209
- with Progress(
210
- SpinnerColumn(),
211
- TextColumn("[progress.description]{task.description}"),
212
- BarColumn(),
213
- TaskProgressColumn(),
214
- TimeElapsedColumn(),
215
- TimeRemainingColumn(compact=True),
216
- refresh_per_second=10,
217
- ) as progress_bar:
218
- task_id = None
219
- if self._duration_secs:
220
- task_id = progress_bar.add_task(
221
- f"Processing {self._input_filepath.name}",
222
- total=self._duration_secs,
223
- )
224
-
225
- while process.poll() is None:
226
- # stdout
227
- if task_id is not None and not stdout_queue.empty():
228
- try:
229
- line = stdout_queue.get_nowait()
230
- if line.startswith(self.TIME_MS_PREFIX):
231
- try:
232
- value = int(line.split("=")[1]) / 1_000_000
233
- if value <= self._duration_secs:
234
- progress_bar.update(task_id, completed=value)
235
- except ValueError:
236
- pass
237
- except Empty:
238
- pass
239
-
240
- # stderr
241
- if not stderr_queue.empty():
242
- try:
243
- self._process_stderr(stderr_queue.get_nowait())
244
- except Empty:
245
- pass
246
-
247
- # Process remaining stderr
248
- while not stderr_queue.empty():
249
- try:
250
- self._process_stderr(stderr_queue.get_nowait())
251
- except Empty:
252
- break
253
-
254
- progress_bar.columns = (
255
- TextColumn("[progress.description]{task.description}"),
256
- )
257
-
258
- if process.returncode != 0:
259
- progress_bar.update(
260
- task_id,
261
- description=f"The FFmpeg process did not complete successfully. Check out {self._ffmpeg_log_file} for details.",
262
- )
263
- return 1
264
-
265
- progress_bar.update(
266
- task_id,
267
- description=f"✓ Processed {self._input_filepath.name}",
268
- )
269
-
319
+ if use_tqdm:
320
+ return self._use_tqdm(process, stdout_queue, stderr_queue)
321
+ else:
322
+ return self._use_rich(process, stdout_queue, stderr_queue)
270
323
  except KeyboardInterrupt:
271
324
  try:
272
325
  psutil.Process(process.pid).terminate()
273
326
  except psutil.NoSuchProcess:
274
327
  pass
275
328
  return 1
276
-
277
- return 0 if process.returncode == 0 else 1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: better-ffmpeg-progress
3
- Version: 3.1.0
3
+ Version: 3.2.1
4
4
  Summary: Run FFmpeg & see percentage progress + ETA.
5
5
  Home-page: https://github.com/CrypticSignal/better-ffmpeg-progress
6
6
  Author: GitHub.com/CrypticSignal
@@ -15,6 +15,7 @@ License-File: LICENSE
15
15
  Requires-Dist: ffmpeg-python
16
16
  Requires-Dist: psutil
17
17
  Requires-Dist: rich
18
+ Requires-Dist: tqdm
18
19
 
19
20
  <div align="center">
20
21
 
@@ -25,6 +26,8 @@ Requires-Dist: rich
25
26
 
26
27
  # Better FFmpeg Progress
27
28
  Runs an FFmpeg command and shows a progress bar with percentage progress, time elapsed and ETA.
29
+
30
+ The [Rich](https://github.com/Textualize/rich) library is used for the progress bar by default, [tqdm](https://github.com/tqdm/tqdm) will be used if you pass `use_tqdm=True` to the `run` method.
28
31
  </div>
29
32
 
30
33
  FFmpeg outputs something like:
@@ -38,7 +41,7 @@ Better FFmpeg Progress outputs something like:
38
41
  Where:
39
42
  - `Processing abc.webm` is the description of the progresss bar.
40
43
  - `23%` is the percentage progress.
41
- - `0:00:04` is the time (H:MM:SS) elapsed.
44
+ - `0:00:04` is the time elapsed.
42
45
  - `00:15` is the estimated time until the FFmpeg process completes.
43
46
 
44
47
  As you can see, the output of Better FFmpeg Progress is much more useful.
@@ -56,24 +59,17 @@ Here's a simple example:
56
59
  from better_ffmpeg_progress import FfmpegProcess
57
60
 
58
61
  process = FfmpegProcess(["ffmpeg", "-i", "input.mp4", "-c:v", "libx265", "output.mp4"])
59
-
60
- # return_code will be 0 if the process was successful
62
+ # return_code will be 0 if the process completed successfully, otherwise it will be 1
61
63
  return_code = process.run()
62
-
63
- if return_code == 0:
64
- # Code to run if the process was successful
65
- pass
66
- else:
67
- # Code to run if the process was unsuccessful
68
- pass
69
64
  ```
70
65
  ## Optional Arguments
71
66
  An instance of the `FfmpegProcess` class takes the following **optional** arguments:
72
67
 
73
68
  - `ffmpeg_log_level` - Desired FFmpeg log level. Default: `"verbose"`
74
- - `ffmpeg_log_file` - The filepath to save the FFmpeg log to.
75
- - `print_detected_duration` - Print the detected duration of the input file. Default: `True`
69
+ - `ffmpeg_log_file` - The filepath to save the FFmpeg log to. Default: `<input filename>_log.txt`
70
+ - `print_detected_duration` - Print the detected duration of the input file. Default: `False`
76
71
  - `print_stderr_new_line` - If better progress information cannot be shown, print FFmpeg stderr in a new line instead of replacing the current line in the terminal. Default: `False`
77
72
 
78
- The `run` method takes the following **optional** argument:
73
+ The `run` method takes the following **optional** arguments:
79
74
  - `print_command` - Print the FFmpeg command being executed. Default: `False`
75
+ - `use_tqdm` - Use [tqdm](https://github.com/tqdm/tqdm) instead of [Rich](https://github.com/Textualize/rich) for the progress bar. Default: `False`
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as f:
5
5
 
6
6
  setuptools.setup(
7
7
  name="better-ffmpeg-progress",
8
- version="3.1.0",
8
+ version="3.2.1",
9
9
  author="GitHub.com/CrypticSignal",
10
10
  author_email="theaudiophile@outlook.com",
11
11
  description="Run FFmpeg & see percentage progress + ETA.",
@@ -13,7 +13,7 @@ setuptools.setup(
13
13
  long_description_content_type="text/markdown",
14
14
  url="https://github.com/CrypticSignal/better-ffmpeg-progress",
15
15
  packages=["better_ffmpeg_progress"],
16
- install_requires=["ffmpeg-python", "psutil", "rich"],
16
+ install_requires=["ffmpeg-python", "psutil", "rich", "tqdm"],
17
17
  python_requires=">=3.7",
18
18
  keywords=["ffmpeg", "progress"],
19
19
  classifiers=[