better-ffmpeg-progress 5.1.0__tar.gz → 5.2.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.
- better_ffmpeg_progress-5.2.0/PKG-INFO +127 -0
- better_ffmpeg_progress-5.2.0/README.md +108 -0
- {better_ffmpeg_progress-5.1.0 → better_ffmpeg_progress-5.2.0}/better_ffmpeg_progress/better_ffmpeg_progress.py +62 -32
- {better_ffmpeg_progress-5.1.0 → better_ffmpeg_progress-5.2.0}/better_ffmpeg_progress/progress_bars.py +5 -13
- {better_ffmpeg_progress-5.1.0 → better_ffmpeg_progress-5.2.0}/better_ffmpeg_progress/terminate_process.py +11 -33
- {better_ffmpeg_progress-5.1.0 → better_ffmpeg_progress-5.2.0}/better_ffmpeg_progress/utils.py +3 -11
- better_ffmpeg_progress-5.2.0/better_ffmpeg_progress.egg-info/PKG-INFO +127 -0
- {better_ffmpeg_progress-5.1.0 → better_ffmpeg_progress-5.2.0}/setup.py +1 -1
- better_ffmpeg_progress-5.1.0/PKG-INFO +0 -94
- better_ffmpeg_progress-5.1.0/README.md +0 -75
- better_ffmpeg_progress-5.1.0/better_ffmpeg_progress.egg-info/PKG-INFO +0 -94
- {better_ffmpeg_progress-5.1.0 → better_ffmpeg_progress-5.2.0}/LICENSE +0 -0
- {better_ffmpeg_progress-5.1.0 → better_ffmpeg_progress-5.2.0}/better_ffmpeg_progress/__init__.py +0 -0
- {better_ffmpeg_progress-5.1.0 → better_ffmpeg_progress-5.2.0}/better_ffmpeg_progress/enums.py +0 -0
- {better_ffmpeg_progress-5.1.0 → better_ffmpeg_progress-5.2.0}/better_ffmpeg_progress/exceptions.py +0 -0
- {better_ffmpeg_progress-5.1.0 → better_ffmpeg_progress-5.2.0}/better_ffmpeg_progress.egg-info/SOURCES.txt +0 -0
- {better_ffmpeg_progress-5.1.0 → better_ffmpeg_progress-5.2.0}/better_ffmpeg_progress.egg-info/dependency_links.txt +0 -0
- {better_ffmpeg_progress-5.1.0 → better_ffmpeg_progress-5.2.0}/better_ffmpeg_progress.egg-info/requires.txt +0 -0
- {better_ffmpeg_progress-5.1.0 → better_ffmpeg_progress-5.2.0}/better_ffmpeg_progress.egg-info/top_level.txt +0 -0
- {better_ffmpeg_progress-5.1.0 → better_ffmpeg_progress-5.2.0}/setup.cfg +0 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: better-ffmpeg-progress
|
|
3
|
+
Version: 5.2.0
|
|
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
|
+
<div align="center">
|
|
21
|
+
[](https://pypistats.org/packages/better-ffmpeg-progress)
|
|
22
|
+
[](https://pypistats.org/packages/better-ffmpeg-progress)
|
|
23
|
+

|
|
24
|
+
[](LICENSE.txt)
|
|
25
|
+
|
|
26
|
+
# Better FFmpeg Progress
|
|
27
|
+
Runs an FFmpeg command and shows a progress bar with percentage progress, elapsed time and ETA.
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
FFmpeg outputs something like:
|
|
31
|
+
```
|
|
32
|
+
frame= 692 fps= 58 q=28.0 size= 5376KiB time=00:00:28.77 bitrate=1530.3kbits/s speed=2.43x
|
|
33
|
+
```
|
|
34
|
+
Better FFmpeg Progress outputs something like:
|
|
35
|
+
```
|
|
36
|
+
⠏ Processing abc.webm ━━━━━━━━━╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 23% 0:00:04 00:15
|
|
37
|
+
```
|
|
38
|
+
Where:
|
|
39
|
+
- `Processing abc.webm` is the description of the progresss bar.
|
|
40
|
+
- `23%` is the percentage progress.
|
|
41
|
+
- `0:00:04` is the time elapsed.
|
|
42
|
+
- `00:15` is the estimated time until the FFmpeg process completes.
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
```bash
|
|
46
|
+
pip install better-ffmpeg-progress --upgrade
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Usage
|
|
50
|
+
Create an instance of the `FfmpegProcess` class and supply a list of arguments like you would when using `subprocess.run()` or `subprocess.Popen()`. Example:
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from better_ffmpeg_progress import FfmpegProcess, FfmpegProcessError
|
|
54
|
+
|
|
55
|
+
command = [
|
|
56
|
+
"ffmpeg",
|
|
57
|
+
"-i",
|
|
58
|
+
"https://media.xiph.org/video/derf/y4m/ducks_take_off_1080p50.y4m",
|
|
59
|
+
"-map",
|
|
60
|
+
"0:V",
|
|
61
|
+
"-c:V",
|
|
62
|
+
"libx264",
|
|
63
|
+
"-preset",
|
|
64
|
+
"ultrafast",
|
|
65
|
+
"-f",
|
|
66
|
+
"null",
|
|
67
|
+
"-",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
process = FfmpegProcess(command)
|
|
72
|
+
# Uncomment this line to use tqdm instead of Rich.
|
|
73
|
+
# process.use_tqdm = True
|
|
74
|
+
|
|
75
|
+
process.run()
|
|
76
|
+
except FfmpegProcessError as e:
|
|
77
|
+
print(
|
|
78
|
+
"An error occurred when running Better FFmpeg Progress:\n"
|
|
79
|
+
f"{e}"
|
|
80
|
+
)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Duration Override
|
|
84
|
+
By default, Better FFmpeg Progress detects the duration of the input file and uses it to calculate percentage progress and ETA.
|
|
85
|
+
|
|
86
|
+
For partial, damaged, streamed or synthetic inputs, FFmpeg may be unable to detect the duration correctly. In these cases, you can provide the duration manually using `duration_override`.
|
|
87
|
+
|
|
88
|
+
The value should be the duration of the input file, in seconds:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from better_ffmpeg_progress import FfmpegProcess
|
|
92
|
+
|
|
93
|
+
command = [
|
|
94
|
+
"ffmpeg",
|
|
95
|
+
"-i",
|
|
96
|
+
"partial-input.flv",
|
|
97
|
+
"-c",
|
|
98
|
+
"copy",
|
|
99
|
+
"recovered-output.mkv",
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
process = FfmpegProcess(
|
|
103
|
+
command,
|
|
104
|
+
duration_override=1222.64,
|
|
105
|
+
print_detected_duration=True,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
process.run()
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Optional Arguments
|
|
112
|
+
An instance of `FfmpegProcess` accepts the following optional arguments:
|
|
113
|
+
* `ffmpeg_log_level` — Set a value for FFmpeg's `-loglevel` option. Default: `"verbose"`.
|
|
114
|
+
* `ffmpeg_log_file` — Filepath or file-like object to which FFmpeg stderr will be written. Default: `<input filename>_ffmpeg_log.txt`.
|
|
115
|
+
* `print_detected_duration` — Print the detected duration. Default: `False`.
|
|
116
|
+
* `duration_override` — Specify input file duration in seconds to use for progress calculation, instead of trying to detect this automatically with FFprobe. Default: `None`.
|
|
117
|
+
|
|
118
|
+
The `run` method accepts the following optional argument:
|
|
119
|
+
* `print_command` — Print the FFmpeg command being executed. Default: `False`.
|
|
120
|
+
|
|
121
|
+
## Using tqdm
|
|
122
|
+
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`:
|
|
123
|
+
```python
|
|
124
|
+
process = FfmpegProcess(command)
|
|
125
|
+
process.use_tqdm = True
|
|
126
|
+
process.run()
|
|
127
|
+
```
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
[](https://pypistats.org/packages/better-ffmpeg-progress)
|
|
3
|
+
[](https://pypistats.org/packages/better-ffmpeg-progress)
|
|
4
|
+

|
|
5
|
+
[](LICENSE.txt)
|
|
6
|
+
|
|
7
|
+
# Better FFmpeg Progress
|
|
8
|
+
Runs an FFmpeg command and shows a progress bar with percentage progress, elapsed time and ETA.
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
FFmpeg outputs something like:
|
|
12
|
+
```
|
|
13
|
+
frame= 692 fps= 58 q=28.0 size= 5376KiB time=00:00:28.77 bitrate=1530.3kbits/s speed=2.43x
|
|
14
|
+
```
|
|
15
|
+
Better FFmpeg Progress outputs something like:
|
|
16
|
+
```
|
|
17
|
+
⠏ Processing abc.webm ━━━━━━━━━╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 23% 0:00:04 00:15
|
|
18
|
+
```
|
|
19
|
+
Where:
|
|
20
|
+
- `Processing abc.webm` is the description of the progresss bar.
|
|
21
|
+
- `23%` is the percentage progress.
|
|
22
|
+
- `0:00:04` is the time elapsed.
|
|
23
|
+
- `00:15` is the estimated time until the FFmpeg process completes.
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
```bash
|
|
27
|
+
pip install better-ffmpeg-progress --upgrade
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
Create an instance of the `FfmpegProcess` class and supply a list of arguments like you would when using `subprocess.run()` or `subprocess.Popen()`. Example:
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from better_ffmpeg_progress import FfmpegProcess, FfmpegProcessError
|
|
35
|
+
|
|
36
|
+
command = [
|
|
37
|
+
"ffmpeg",
|
|
38
|
+
"-i",
|
|
39
|
+
"https://media.xiph.org/video/derf/y4m/ducks_take_off_1080p50.y4m",
|
|
40
|
+
"-map",
|
|
41
|
+
"0:V",
|
|
42
|
+
"-c:V",
|
|
43
|
+
"libx264",
|
|
44
|
+
"-preset",
|
|
45
|
+
"ultrafast",
|
|
46
|
+
"-f",
|
|
47
|
+
"null",
|
|
48
|
+
"-",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
process = FfmpegProcess(command)
|
|
53
|
+
# Uncomment this line to use tqdm instead of Rich.
|
|
54
|
+
# process.use_tqdm = True
|
|
55
|
+
|
|
56
|
+
process.run()
|
|
57
|
+
except FfmpegProcessError as e:
|
|
58
|
+
print(
|
|
59
|
+
"An error occurred when running Better FFmpeg Progress:\n"
|
|
60
|
+
f"{e}"
|
|
61
|
+
)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Duration Override
|
|
65
|
+
By default, Better FFmpeg Progress detects the duration of the input file and uses it to calculate percentage progress and ETA.
|
|
66
|
+
|
|
67
|
+
For partial, damaged, streamed or synthetic inputs, FFmpeg may be unable to detect the duration correctly. In these cases, you can provide the duration manually using `duration_override`.
|
|
68
|
+
|
|
69
|
+
The value should be the duration of the input file, in seconds:
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from better_ffmpeg_progress import FfmpegProcess
|
|
73
|
+
|
|
74
|
+
command = [
|
|
75
|
+
"ffmpeg",
|
|
76
|
+
"-i",
|
|
77
|
+
"partial-input.flv",
|
|
78
|
+
"-c",
|
|
79
|
+
"copy",
|
|
80
|
+
"recovered-output.mkv",
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
process = FfmpegProcess(
|
|
84
|
+
command,
|
|
85
|
+
duration_override=1222.64,
|
|
86
|
+
print_detected_duration=True,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
process.run()
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Optional Arguments
|
|
93
|
+
An instance of `FfmpegProcess` accepts the following optional arguments:
|
|
94
|
+
* `ffmpeg_log_level` — Set a value for FFmpeg's `-loglevel` option. Default: `"verbose"`.
|
|
95
|
+
* `ffmpeg_log_file` — Filepath or file-like object to which FFmpeg stderr will be written. Default: `<input filename>_ffmpeg_log.txt`.
|
|
96
|
+
* `print_detected_duration` — Print the detected duration. Default: `False`.
|
|
97
|
+
* `duration_override` — Specify input file duration in seconds to use for progress calculation, instead of trying to detect this automatically with FFprobe. Default: `None`.
|
|
98
|
+
|
|
99
|
+
The `run` method accepts the following optional argument:
|
|
100
|
+
* `print_command` — Print the FFmpeg command being executed. Default: `False`.
|
|
101
|
+
|
|
102
|
+
## Using tqdm
|
|
103
|
+
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`:
|
|
104
|
+
```python
|
|
105
|
+
process = FfmpegProcess(command)
|
|
106
|
+
process.use_tqdm = True
|
|
107
|
+
process.run()
|
|
108
|
+
```
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import math
|
|
1
2
|
import os
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
import subprocess
|
|
@@ -27,18 +28,16 @@ class FfmpegProcess:
|
|
|
27
28
|
"""
|
|
28
29
|
Handles the logic for overwriting an existing output file.
|
|
29
30
|
Modifies self._ffmpeg_command to include -y if overwrite is confirmed.
|
|
30
|
-
Raises FfmpegProcessUserCancelledError or
|
|
31
|
+
Raises FfmpegProcessUserCancelledError or
|
|
32
|
+
FfmpegProcessInterruptedError if not proceeding.
|
|
31
33
|
"""
|
|
32
34
|
try:
|
|
33
35
|
answer = (
|
|
34
|
-
input(
|
|
35
|
-
f"Output file {self._output_filepath} already exists. Overwrite? [y/N]: "
|
|
36
|
-
)
|
|
36
|
+
input(f"Output file {self._output_filepath} already exists. Overwrite? [y/N]: ")
|
|
37
37
|
.strip()
|
|
38
38
|
.lower()
|
|
39
39
|
)
|
|
40
40
|
if answer == "y":
|
|
41
|
-
# Insert -y
|
|
42
41
|
self._ffmpeg_command.insert(1, _FFMPEG_OVERWRITE_FLAG)
|
|
43
42
|
else:
|
|
44
43
|
raise FfmpegProcessUserCancelledError(
|
|
@@ -59,8 +58,24 @@ class FfmpegProcess:
|
|
|
59
58
|
ffmpeg_log_level: Optional[Union[FfmpegLogLevel, str]] = None,
|
|
60
59
|
ffmpeg_log_file: Optional[Union[str, os.PathLike]] = None,
|
|
61
60
|
print_detected_duration: bool = False,
|
|
61
|
+
duration_override: Optional[float] = None,
|
|
62
62
|
):
|
|
63
|
-
|
|
63
|
+
"""
|
|
64
|
+
Create an FFmpeg process with progress reporting.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
command:
|
|
68
|
+
FFmpeg command as a list of arguments.
|
|
69
|
+
ffmpeg_log_level:
|
|
70
|
+
FFmpeg log level enum or case-insensitive string.
|
|
71
|
+
ffmpeg_log_file:
|
|
72
|
+
File path or file-like object used for FFmpeg stderr.
|
|
73
|
+
print_detected_duration:
|
|
74
|
+
Print the duration used for progress calculation.
|
|
75
|
+
duration_override:
|
|
76
|
+
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
|
|
77
|
+
synthetic inputs whose duration cannot be detected correctly.
|
|
78
|
+
"""
|
|
64
79
|
validate_ffmpeg_command(command)
|
|
65
80
|
|
|
66
81
|
ffmpeg_log_level_val: str
|
|
@@ -75,20 +90,24 @@ class FfmpegProcess:
|
|
|
75
90
|
except ValueError:
|
|
76
91
|
valid_levels = [e.value for e in FfmpegLogLevel]
|
|
77
92
|
raise FfmpegCommandError(
|
|
78
|
-
f"Invalid ffmpeg_log_level string:
|
|
79
|
-
f"Must be one of
|
|
93
|
+
f"Invalid ffmpeg_log_level string: "
|
|
94
|
+
f"'{ffmpeg_log_level}'. Must be one of "
|
|
95
|
+
f"{valid_levels} (case-insensitive)."
|
|
80
96
|
)
|
|
81
97
|
else:
|
|
82
98
|
raise TypeError(
|
|
83
|
-
|
|
84
|
-
f"not
|
|
99
|
+
"ffmpeg_log_level must be an FfmpegLogLevel enum instance, "
|
|
100
|
+
f"a string, or None, not "
|
|
101
|
+
f"{type(ffmpeg_log_level).__name__}"
|
|
85
102
|
)
|
|
103
|
+
|
|
86
104
|
self._ffmpeg_log_level_val = ffmpeg_log_level_val
|
|
87
105
|
|
|
88
106
|
input_file_index = command.index("-i")
|
|
89
107
|
input_file_path_str = command[input_file_index + 1]
|
|
90
108
|
self._input_filepath = Path(input_file_path_str)
|
|
91
|
-
|
|
109
|
+
|
|
110
|
+
# Assumes last argument is output.
|
|
92
111
|
self._output_filepath = Path(command[-1])
|
|
93
112
|
|
|
94
113
|
if ffmpeg_log_file is None:
|
|
@@ -99,14 +118,30 @@ class FfmpegProcess:
|
|
|
99
118
|
self._ffmpeg_log_file = ffmpeg_log_file
|
|
100
119
|
|
|
101
120
|
self._print_detected_duration = print_detected_duration
|
|
102
|
-
self._duration_secs = get_media_duration(input_file_path_str)
|
|
103
121
|
|
|
104
|
-
if
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
122
|
+
if duration_override is not None:
|
|
123
|
+
if isinstance(duration_override, bool):
|
|
124
|
+
raise TypeError("duration_override must be a number of seconds, not bool")
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
duration_value = float(duration_override)
|
|
128
|
+
except (TypeError, ValueError) as e:
|
|
129
|
+
raise TypeError("duration_override must be a number of seconds or None") from e
|
|
130
|
+
|
|
131
|
+
if not math.isfinite(duration_value) or duration_value <= 0:
|
|
132
|
+
raise ValueError("duration_override must be a finite number greater than 0")
|
|
133
|
+
|
|
134
|
+
self._duration_secs = duration_value
|
|
135
|
+
else:
|
|
136
|
+
self._duration_secs = get_media_duration(input_file_path_str)
|
|
137
|
+
|
|
138
|
+
if duration_override is not None:
|
|
139
|
+
print(f"Using duration override: {self._duration_secs:.2f} seconds")
|
|
140
|
+
elif self._print_detected_duration:
|
|
141
|
+
if self._duration_secs is None:
|
|
142
|
+
print("Could not detect duration. Progress bar may not show time remaining.")
|
|
143
|
+
else:
|
|
144
|
+
print(f"Detected duration: {self._duration_secs:.2f} seconds")
|
|
110
145
|
|
|
111
146
|
self._ffmpeg_command = [
|
|
112
147
|
command[0],
|
|
@@ -114,14 +149,12 @@ class FfmpegProcess:
|
|
|
114
149
|
"-loglevel",
|
|
115
150
|
self._ffmpeg_log_level_val,
|
|
116
151
|
"-progress",
|
|
117
|
-
"pipe:1",
|
|
152
|
+
"pipe:1",
|
|
118
153
|
"-nostats",
|
|
119
154
|
]
|
|
120
155
|
self._ffmpeg_command.extend(command[1:])
|
|
121
156
|
|
|
122
|
-
is_overwrite_in_user_command = any(
|
|
123
|
-
arg == _FFMPEG_OVERWRITE_FLAG for arg in command[1:]
|
|
124
|
-
)
|
|
157
|
+
is_overwrite_in_user_command = any(arg == _FFMPEG_OVERWRITE_FLAG for arg in command[1:])
|
|
125
158
|
|
|
126
159
|
if self._output_filepath.exists() and not is_overwrite_in_user_command:
|
|
127
160
|
self._handle_overwrite_prompt()
|
|
@@ -158,7 +191,11 @@ class FfmpegProcess:
|
|
|
158
191
|
creationflags = subprocess.CREATE_NEW_PROCESS_GROUP
|
|
159
192
|
|
|
160
193
|
if isinstance(self._ffmpeg_log_file, Path):
|
|
161
|
-
with open(
|
|
194
|
+
with open(
|
|
195
|
+
self._ffmpeg_log_file,
|
|
196
|
+
"w",
|
|
197
|
+
encoding="utf-8",
|
|
198
|
+
) as f:
|
|
162
199
|
self._process = subprocess.Popen(
|
|
163
200
|
current_ffmpeg_command,
|
|
164
201
|
shell=self._shell_needed,
|
|
@@ -177,26 +214,19 @@ class FfmpegProcess:
|
|
|
177
214
|
except Exception as e:
|
|
178
215
|
raise FfmpegProcessError(f"Error starting FFmpeg process: {e}") from e
|
|
179
216
|
|
|
180
|
-
user_interrupted = False
|
|
181
217
|
try:
|
|
182
218
|
if self.use_tqdm:
|
|
183
219
|
use_tqdm(self, self._process)
|
|
184
220
|
else:
|
|
185
221
|
use_rich(self, self._process)
|
|
186
222
|
except KeyboardInterrupt:
|
|
187
|
-
user_interrupted = True
|
|
188
223
|
self._terminate()
|
|
189
224
|
finally:
|
|
190
|
-
if self._process:
|
|
191
|
-
|
|
192
|
-
self._process.stdout.close()
|
|
193
|
-
|
|
194
|
-
if not user_interrupted and self._process and self._process.returncode != 0:
|
|
195
|
-
raise FfmpegProcessError(f"FFmpeg process failed. Return code: {self._process.returncode}")
|
|
225
|
+
if self._process and self._process.stdout:
|
|
226
|
+
self._process.stdout.close()
|
|
196
227
|
|
|
197
228
|
def _terminate(self):
|
|
198
229
|
if self._process:
|
|
199
230
|
terminate_ffmpeg_process(self._process)
|
|
200
231
|
else:
|
|
201
232
|
exit()
|
|
202
|
-
|
|
@@ -19,9 +19,7 @@ if TYPE_CHECKING:
|
|
|
19
19
|
from .better_ffmpeg_progress import FfmpegProcess
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
def use_rich(
|
|
23
|
-
ffmpeg_process_instance: "FfmpegProcess", process: subprocess.Popen
|
|
24
|
-
) -> None:
|
|
22
|
+
def use_rich(ffmpeg_process_instance: "FfmpegProcess", process: subprocess.Popen) -> None:
|
|
25
23
|
task_id = None
|
|
26
24
|
progress_bar_instance = Progress(
|
|
27
25
|
TextColumn("[progress.description]{task.description}"),
|
|
@@ -39,16 +37,14 @@ def use_rich(
|
|
|
39
37
|
with progress_bar_instance as progress_bar:
|
|
40
38
|
if duration_secs:
|
|
41
39
|
task_id = progress_bar.add_task(
|
|
42
|
-
f"Processing
|
|
40
|
+
f"Processing {input_filename}",
|
|
43
41
|
total=duration_secs,
|
|
44
42
|
)
|
|
45
43
|
update_progress = progress_bar.update
|
|
46
44
|
|
|
47
45
|
for line_bytes in process.stdout:
|
|
48
46
|
stripped_line_bytes = line_bytes.strip()
|
|
49
|
-
progress_val_secs = parse_ffmpeg_progress_line(
|
|
50
|
-
stripped_line_bytes, duration_secs
|
|
51
|
-
)
|
|
47
|
+
progress_val_secs = parse_ffmpeg_progress_line(stripped_line_bytes, duration_secs)
|
|
52
48
|
if progress_val_secs is not None and task_id is not None:
|
|
53
49
|
update_progress(task_id, completed=progress_val_secs)
|
|
54
50
|
else:
|
|
@@ -75,9 +71,7 @@ def use_rich(
|
|
|
75
71
|
raise FfmpegProcessError(f"FFmpeg process failed. Check '{log_file}' for details.")
|
|
76
72
|
|
|
77
73
|
|
|
78
|
-
def use_tqdm(
|
|
79
|
-
ffmpeg_process_instance: "FfmpegProcess", process: subprocess.Popen
|
|
80
|
-
) -> None:
|
|
74
|
+
def use_tqdm(ffmpeg_process_instance: "FfmpegProcess", process: subprocess.Popen) -> None:
|
|
81
75
|
progress_bar = None
|
|
82
76
|
try:
|
|
83
77
|
width = os.get_terminal_size().columns
|
|
@@ -100,9 +94,7 @@ def use_tqdm(
|
|
|
100
94
|
|
|
101
95
|
for line_bytes in process.stdout:
|
|
102
96
|
stripped_line_bytes = line_bytes.strip()
|
|
103
|
-
progress_val_secs = parse_ffmpeg_progress_line(
|
|
104
|
-
stripped_line_bytes, duration_secs
|
|
105
|
-
)
|
|
97
|
+
progress_val_secs = parse_ffmpeg_progress_line(stripped_line_bytes, duration_secs)
|
|
106
98
|
if progress_val_secs is not None and progress_bar is not None:
|
|
107
99
|
progress_bar.n = progress_val_secs
|
|
108
100
|
progress_bar.refresh()
|
|
@@ -32,9 +32,7 @@ def _terminate_children_processes(parent_proc: psutil.Process):
|
|
|
32
32
|
if not children:
|
|
33
33
|
return
|
|
34
34
|
|
|
35
|
-
print(
|
|
36
|
-
f"Attempting to terminate {len(children)} child process(es) of {parent_proc.pid}..."
|
|
37
|
-
)
|
|
35
|
+
print(f"Attempting to terminate {len(children)} child process(es) of {parent_proc.pid}...")
|
|
38
36
|
for child in children:
|
|
39
37
|
try:
|
|
40
38
|
if child.is_running():
|
|
@@ -68,21 +66,15 @@ def _terminate_parent_process_windows(proc: psutil.Process):
|
|
|
68
66
|
try:
|
|
69
67
|
# Check the status after the timeout
|
|
70
68
|
if proc.is_running():
|
|
71
|
-
print(
|
|
72
|
-
f"FFmpeg process {proc.pid} still running after {_TERMINATION_TIMEOUT}s."
|
|
73
|
-
)
|
|
69
|
+
print(f"FFmpeg process {proc.pid} still running after {_TERMINATION_TIMEOUT}s.")
|
|
74
70
|
else:
|
|
75
|
-
print(
|
|
76
|
-
f"FFmpeg process termination took longer than {_TERMINATION_TIMEOUT}s."
|
|
77
|
-
)
|
|
71
|
+
print(f"FFmpeg process termination took longer than {_TERMINATION_TIMEOUT}s.")
|
|
78
72
|
except psutil.NoSuchProcess:
|
|
79
73
|
print(
|
|
80
74
|
f"FFmpeg process not found after {_TERMINATION_TIMEOUT}s. It seems like it has terminated."
|
|
81
75
|
)
|
|
82
76
|
except psutil.Error as e:
|
|
83
|
-
print(
|
|
84
|
-
f"Error during status check for process {proc.pid} after timeout:\n{e}"
|
|
85
|
-
)
|
|
77
|
+
print(f"Error during status check for process {proc.pid} after timeout:\n{e}")
|
|
86
78
|
except psutil.AccessDenied:
|
|
87
79
|
print(f"Access denied when trying to terminate FFmpeg process {proc.pid}.")
|
|
88
80
|
except psutil.Error as e:
|
|
@@ -106,14 +98,10 @@ def _terminate_parent_process_unix(proc: psutil.Process, original_pid: int):
|
|
|
106
98
|
if not proc.is_running():
|
|
107
99
|
terminated_by_pg = True
|
|
108
100
|
except psutil.TimeoutExpired:
|
|
109
|
-
print(
|
|
110
|
-
f"Process group {pgid_to_signal} did not exit after SIGTERM, sending SIGKILL..."
|
|
111
|
-
)
|
|
101
|
+
print(f"Process group {pgid_to_signal} did not exit after SIGTERM, sending SIGKILL...")
|
|
112
102
|
os.killpg(pgid_to_signal, SIGKILL)
|
|
113
103
|
try:
|
|
114
|
-
proc.wait(
|
|
115
|
-
timeout=_TERMINATION_TIMEOUT
|
|
116
|
-
) # Wait for SIGKILL on process group
|
|
104
|
+
proc.wait(timeout=_TERMINATION_TIMEOUT) # Wait for SIGKILL on process group
|
|
117
105
|
if not proc.is_running():
|
|
118
106
|
terminated_by_pg = True
|
|
119
107
|
except psutil.TimeoutExpired:
|
|
@@ -132,13 +120,9 @@ def _terminate_parent_process_unix(proc: psutil.Process, original_pid: int):
|
|
|
132
120
|
# os.killpg failed
|
|
133
121
|
except OSError as e:
|
|
134
122
|
target_desc = (
|
|
135
|
-
f"process group {pgid_to_signal}"
|
|
136
|
-
if pgid_to_signal != 0
|
|
137
|
-
else f"process {original_pid}"
|
|
138
|
-
)
|
|
139
|
-
print(
|
|
140
|
-
f"Error sending signal to {target_desc}: {e}. Fallback to individual process."
|
|
123
|
+
f"process group {pgid_to_signal}" if pgid_to_signal != 0 else f"process {original_pid}"
|
|
141
124
|
)
|
|
125
|
+
print(f"Error sending signal to {target_desc}: {e}. Fallback to individual process.")
|
|
142
126
|
|
|
143
127
|
if terminated_by_pg:
|
|
144
128
|
return
|
|
@@ -159,9 +143,7 @@ def _terminate_parent_process_unix(proc: psutil.Process, original_pid: int):
|
|
|
159
143
|
f"SIGTERM for FFmpeg process (PID: {proc.pid}) timed out after {_TERMINATION_TIMEOUT}s."
|
|
160
144
|
)
|
|
161
145
|
except psutil.Error as e:
|
|
162
|
-
print(
|
|
163
|
-
f"Error during SIGTERM for FFmpeg process (PID: {proc.pid}):\n{e}"
|
|
164
|
-
)
|
|
146
|
+
print(f"Error during SIGTERM for FFmpeg process (PID: {proc.pid}):\n{e}")
|
|
165
147
|
if not proc.is_running():
|
|
166
148
|
return
|
|
167
149
|
|
|
@@ -177,9 +159,7 @@ def _terminate_parent_process_unix(proc: psutil.Process, original_pid: int):
|
|
|
177
159
|
proc.kill()
|
|
178
160
|
print(f"SIGKILL sent to FFmpeg process (PID: {proc.pid}).")
|
|
179
161
|
except psutil.Error as e:
|
|
180
|
-
print(
|
|
181
|
-
f"Error during SIGKILL for FFmpeg process (PID: {proc.pid}):\n{e}"
|
|
182
|
-
)
|
|
162
|
+
print(f"Error during SIGKILL for FFmpeg process (PID: {proc.pid}):\n{e}")
|
|
183
163
|
else:
|
|
184
164
|
print(f"It seems like FFmpeg process {proc.pid} is no longer running.")
|
|
185
165
|
except psutil.NoSuchProcess:
|
|
@@ -239,9 +219,7 @@ def terminate_ffmpeg_process(process: Optional[subprocess.Popen]):
|
|
|
239
219
|
f"Main FFmpeg process {original_pid} became inaccessible during termination sequence."
|
|
240
220
|
)
|
|
241
221
|
except Exception as e:
|
|
242
|
-
print(
|
|
243
|
-
f"Unexpected error during main process termination for {original_pid}: {e}"
|
|
244
|
-
)
|
|
222
|
+
print(f"Unexpected error during main process termination for {original_pid}: {e}")
|
|
245
223
|
|
|
246
224
|
if process.poll() is None:
|
|
247
225
|
exit("It seems like the FFmpeg process has not terminated.")
|
{better_ffmpeg_progress-5.1.0 → better_ffmpeg_progress-5.2.0}/better_ffmpeg_progress/utils.py
RENAMED
|
@@ -42,16 +42,10 @@ def validate_ffmpeg_command(command: List[str]) -> None:
|
|
|
42
42
|
|
|
43
43
|
input_file_idx = input_flag_pos + 1
|
|
44
44
|
if input_file_idx >= len(command):
|
|
45
|
-
raise FfmpegCommandError(
|
|
46
|
-
f"No input file specified after '{_FFMPEG_INPUT_FLAG}'."
|
|
47
|
-
)
|
|
45
|
+
raise FfmpegCommandError(f"No input file specified after '{_FFMPEG_INPUT_FLAG}'.")
|
|
48
46
|
|
|
49
47
|
input_file = command[input_file_idx]
|
|
50
|
-
if (
|
|
51
|
-
input_file.startswith("-")
|
|
52
|
-
and len(input_file) == 2
|
|
53
|
-
and not input_file[1:].isdigit()
|
|
54
|
-
):
|
|
48
|
+
if input_file.startswith("-") and len(input_file) == 2 and not input_file[1:].isdigit():
|
|
55
49
|
raise FfmpegCommandError(
|
|
56
50
|
f"Input file path '{input_file}' looks like an option flag. Please check your command."
|
|
57
51
|
)
|
|
@@ -62,9 +56,7 @@ def validate_ffmpeg_command(command: List[str]) -> None:
|
|
|
62
56
|
response = requests.head(input_file, timeout=5, allow_redirects=True)
|
|
63
57
|
response.raise_for_status()
|
|
64
58
|
except requests.exceptions.RequestException as e:
|
|
65
|
-
raise FfmpegCommandError(
|
|
66
|
-
f"Input URL not accessible: {input_file}. Error: {e}"
|
|
67
|
-
)
|
|
59
|
+
raise FfmpegCommandError(f"Input URL not accessible: {input_file}. Error: {e}")
|
|
68
60
|
elif not Path(input_file).exists():
|
|
69
61
|
raise FfmpegCommandError(f"Input file not found: {input_file}")
|
|
70
62
|
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: better-ffmpeg-progress
|
|
3
|
+
Version: 5.2.0
|
|
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
|
+
<div align="center">
|
|
21
|
+
[](https://pypistats.org/packages/better-ffmpeg-progress)
|
|
22
|
+
[](https://pypistats.org/packages/better-ffmpeg-progress)
|
|
23
|
+

|
|
24
|
+
[](LICENSE.txt)
|
|
25
|
+
|
|
26
|
+
# Better FFmpeg Progress
|
|
27
|
+
Runs an FFmpeg command and shows a progress bar with percentage progress, elapsed time and ETA.
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
FFmpeg outputs something like:
|
|
31
|
+
```
|
|
32
|
+
frame= 692 fps= 58 q=28.0 size= 5376KiB time=00:00:28.77 bitrate=1530.3kbits/s speed=2.43x
|
|
33
|
+
```
|
|
34
|
+
Better FFmpeg Progress outputs something like:
|
|
35
|
+
```
|
|
36
|
+
⠏ Processing abc.webm ━━━━━━━━━╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 23% 0:00:04 00:15
|
|
37
|
+
```
|
|
38
|
+
Where:
|
|
39
|
+
- `Processing abc.webm` is the description of the progresss bar.
|
|
40
|
+
- `23%` is the percentage progress.
|
|
41
|
+
- `0:00:04` is the time elapsed.
|
|
42
|
+
- `00:15` is the estimated time until the FFmpeg process completes.
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
```bash
|
|
46
|
+
pip install better-ffmpeg-progress --upgrade
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Usage
|
|
50
|
+
Create an instance of the `FfmpegProcess` class and supply a list of arguments like you would when using `subprocess.run()` or `subprocess.Popen()`. Example:
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from better_ffmpeg_progress import FfmpegProcess, FfmpegProcessError
|
|
54
|
+
|
|
55
|
+
command = [
|
|
56
|
+
"ffmpeg",
|
|
57
|
+
"-i",
|
|
58
|
+
"https://media.xiph.org/video/derf/y4m/ducks_take_off_1080p50.y4m",
|
|
59
|
+
"-map",
|
|
60
|
+
"0:V",
|
|
61
|
+
"-c:V",
|
|
62
|
+
"libx264",
|
|
63
|
+
"-preset",
|
|
64
|
+
"ultrafast",
|
|
65
|
+
"-f",
|
|
66
|
+
"null",
|
|
67
|
+
"-",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
process = FfmpegProcess(command)
|
|
72
|
+
# Uncomment this line to use tqdm instead of Rich.
|
|
73
|
+
# process.use_tqdm = True
|
|
74
|
+
|
|
75
|
+
process.run()
|
|
76
|
+
except FfmpegProcessError as e:
|
|
77
|
+
print(
|
|
78
|
+
"An error occurred when running Better FFmpeg Progress:\n"
|
|
79
|
+
f"{e}"
|
|
80
|
+
)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Duration Override
|
|
84
|
+
By default, Better FFmpeg Progress detects the duration of the input file and uses it to calculate percentage progress and ETA.
|
|
85
|
+
|
|
86
|
+
For partial, damaged, streamed or synthetic inputs, FFmpeg may be unable to detect the duration correctly. In these cases, you can provide the duration manually using `duration_override`.
|
|
87
|
+
|
|
88
|
+
The value should be the duration of the input file, in seconds:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from better_ffmpeg_progress import FfmpegProcess
|
|
92
|
+
|
|
93
|
+
command = [
|
|
94
|
+
"ffmpeg",
|
|
95
|
+
"-i",
|
|
96
|
+
"partial-input.flv",
|
|
97
|
+
"-c",
|
|
98
|
+
"copy",
|
|
99
|
+
"recovered-output.mkv",
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
process = FfmpegProcess(
|
|
103
|
+
command,
|
|
104
|
+
duration_override=1222.64,
|
|
105
|
+
print_detected_duration=True,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
process.run()
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Optional Arguments
|
|
112
|
+
An instance of `FfmpegProcess` accepts the following optional arguments:
|
|
113
|
+
* `ffmpeg_log_level` — Set a value for FFmpeg's `-loglevel` option. Default: `"verbose"`.
|
|
114
|
+
* `ffmpeg_log_file` — Filepath or file-like object to which FFmpeg stderr will be written. Default: `<input filename>_ffmpeg_log.txt`.
|
|
115
|
+
* `print_detected_duration` — Print the detected duration. Default: `False`.
|
|
116
|
+
* `duration_override` — Specify input file duration in seconds to use for progress calculation, instead of trying to detect this automatically with FFprobe. Default: `None`.
|
|
117
|
+
|
|
118
|
+
The `run` method accepts the following optional argument:
|
|
119
|
+
* `print_command` — Print the FFmpeg command being executed. Default: `False`.
|
|
120
|
+
|
|
121
|
+
## Using tqdm
|
|
122
|
+
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`:
|
|
123
|
+
```python
|
|
124
|
+
process = FfmpegProcess(command)
|
|
125
|
+
process.use_tqdm = True
|
|
126
|
+
process.run()
|
|
127
|
+
```
|
|
@@ -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="5.
|
|
8
|
+
version="5.2.0",
|
|
9
9
|
author="GitHub.com/CrypticSignal",
|
|
10
10
|
author_email="theaudiophile@outlook.com",
|
|
11
11
|
description="Run FFmpeg & see percentage progress + ETA.",
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: better-ffmpeg-progress
|
|
3
|
-
Version: 5.1.0
|
|
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
|
-
<div align="center">
|
|
21
|
-
|
|
22
|
-
[](https://pypistats.org/packages/better-ffmpeg-progress)
|
|
23
|
-
[](https://pypistats.org/packages/better-ffmpeg-progress)
|
|
24
|
-

|
|
25
|
-
[](LICENSE.txt)
|
|
26
|
-
|
|
27
|
-
# Better FFmpeg Progress
|
|
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.
|
|
31
|
-
</div>
|
|
32
|
-
|
|
33
|
-
FFmpeg outputs something like:
|
|
34
|
-
```
|
|
35
|
-
frame= 692 fps= 58 q=28.0 size= 5376KiB time=00:00:28.77 bitrate=1530.3kbits/s speed=2.43x
|
|
36
|
-
```
|
|
37
|
-
Better FFmpeg Progress outputs something like:
|
|
38
|
-
```
|
|
39
|
-
⠏ Processing abc.webm ━━━━━━━━━╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 23% 0:00:04 00:15
|
|
40
|
-
```
|
|
41
|
-
Where:
|
|
42
|
-
- `Processing abc.webm` is the description of the progresss bar.
|
|
43
|
-
- `23%` is the percentage progress.
|
|
44
|
-
- `0:00:04` is the time elapsed.
|
|
45
|
-
- `00:15` is the estimated time until the FFmpeg process completes.
|
|
46
|
-
|
|
47
|
-
As you can see, the output of Better FFmpeg Progress is much more useful.
|
|
48
|
-
|
|
49
|
-
## Installation
|
|
50
|
-
```
|
|
51
|
-
pip install better-ffmpeg-progress --upgrade
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## Usage
|
|
55
|
-
Create an instance of the `FfmpegProcess` class and supply a list of arguments like you would to `subprocess.run()` or `subprocess.Popen()`.
|
|
56
|
-
|
|
57
|
-
Example:
|
|
58
|
-
```py
|
|
59
|
-
from better_ffmpeg_progress import FfmpegProcess, FfmpegProcessError
|
|
60
|
-
|
|
61
|
-
command = [
|
|
62
|
-
"ffmpeg",
|
|
63
|
-
"-i",
|
|
64
|
-
"https://media.xiph.org/video/derf/y4m/ducks_take_off_1080p50.y4m",
|
|
65
|
-
"-map",
|
|
66
|
-
"0:V",
|
|
67
|
-
"-c:V",
|
|
68
|
-
"libx264",
|
|
69
|
-
"-preset",
|
|
70
|
-
"ultrafast",
|
|
71
|
-
"-f",
|
|
72
|
-
"null",
|
|
73
|
-
"-",
|
|
74
|
-
]
|
|
75
|
-
|
|
76
|
-
try:
|
|
77
|
-
process = FfmpegProcess(command)
|
|
78
|
-
# Uncomment the line below if you want to use tqdm instead of rich for the progress bar
|
|
79
|
-
# process.use_tqdm = True
|
|
80
|
-
|
|
81
|
-
process.run()
|
|
82
|
-
except FfmpegProcessError as e:
|
|
83
|
-
print(f"An error occurred when running the better-ffmpeg-process package:\n{e}")
|
|
84
|
-
```
|
|
85
|
-
## Optional Arguments
|
|
86
|
-
An instance of the `FfmpegProcess` class takes the following **optional** arguments:
|
|
87
|
-
|
|
88
|
-
- `ffmpeg_log_level` - Desired FFmpeg log level. Default: `"verbose"`
|
|
89
|
-
- `ffmpeg_log_file` - The filepath to save the FFmpeg log to. Default: `<input filename>_log.txt`
|
|
90
|
-
- `print_detected_duration` - Print the detected duration of the input file. Default: `False`
|
|
91
|
-
- `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`
|
|
92
|
-
|
|
93
|
-
The `run` method takes the following **optional** arguments:
|
|
94
|
-
- `print_command` - Print the FFmpeg command being executed. Default: `False`
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
<div align="center">
|
|
2
|
-
|
|
3
|
-
[](https://pypistats.org/packages/better-ffmpeg-progress)
|
|
4
|
-
[](https://pypistats.org/packages/better-ffmpeg-progress)
|
|
5
|
-

|
|
6
|
-
[](LICENSE.txt)
|
|
7
|
-
|
|
8
|
-
# Better FFmpeg Progress
|
|
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.
|
|
12
|
-
</div>
|
|
13
|
-
|
|
14
|
-
FFmpeg outputs something like:
|
|
15
|
-
```
|
|
16
|
-
frame= 692 fps= 58 q=28.0 size= 5376KiB time=00:00:28.77 bitrate=1530.3kbits/s speed=2.43x
|
|
17
|
-
```
|
|
18
|
-
Better FFmpeg Progress outputs something like:
|
|
19
|
-
```
|
|
20
|
-
⠏ Processing abc.webm ━━━━━━━━━╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 23% 0:00:04 00:15
|
|
21
|
-
```
|
|
22
|
-
Where:
|
|
23
|
-
- `Processing abc.webm` is the description of the progresss bar.
|
|
24
|
-
- `23%` is the percentage progress.
|
|
25
|
-
- `0:00:04` is the time elapsed.
|
|
26
|
-
- `00:15` is the estimated time until the FFmpeg process completes.
|
|
27
|
-
|
|
28
|
-
As you can see, the output of Better FFmpeg Progress is much more useful.
|
|
29
|
-
|
|
30
|
-
## Installation
|
|
31
|
-
```
|
|
32
|
-
pip install better-ffmpeg-progress --upgrade
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## Usage
|
|
36
|
-
Create an instance of the `FfmpegProcess` class and supply a list of arguments like you would to `subprocess.run()` or `subprocess.Popen()`.
|
|
37
|
-
|
|
38
|
-
Example:
|
|
39
|
-
```py
|
|
40
|
-
from better_ffmpeg_progress import FfmpegProcess, FfmpegProcessError
|
|
41
|
-
|
|
42
|
-
command = [
|
|
43
|
-
"ffmpeg",
|
|
44
|
-
"-i",
|
|
45
|
-
"https://media.xiph.org/video/derf/y4m/ducks_take_off_1080p50.y4m",
|
|
46
|
-
"-map",
|
|
47
|
-
"0:V",
|
|
48
|
-
"-c:V",
|
|
49
|
-
"libx264",
|
|
50
|
-
"-preset",
|
|
51
|
-
"ultrafast",
|
|
52
|
-
"-f",
|
|
53
|
-
"null",
|
|
54
|
-
"-",
|
|
55
|
-
]
|
|
56
|
-
|
|
57
|
-
try:
|
|
58
|
-
process = FfmpegProcess(command)
|
|
59
|
-
# Uncomment the line below if you want to use tqdm instead of rich for the progress bar
|
|
60
|
-
# process.use_tqdm = True
|
|
61
|
-
|
|
62
|
-
process.run()
|
|
63
|
-
except FfmpegProcessError as e:
|
|
64
|
-
print(f"An error occurred when running the better-ffmpeg-process package:\n{e}")
|
|
65
|
-
```
|
|
66
|
-
## Optional Arguments
|
|
67
|
-
An instance of the `FfmpegProcess` class takes the following **optional** arguments:
|
|
68
|
-
|
|
69
|
-
- `ffmpeg_log_level` - Desired FFmpeg log level. Default: `"verbose"`
|
|
70
|
-
- `ffmpeg_log_file` - The filepath to save the FFmpeg log to. Default: `<input filename>_log.txt`
|
|
71
|
-
- `print_detected_duration` - Print the detected duration of the input file. Default: `False`
|
|
72
|
-
- `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`
|
|
73
|
-
|
|
74
|
-
The `run` method takes the following **optional** arguments:
|
|
75
|
-
- `print_command` - Print the FFmpeg command being executed. Default: `False`
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: better-ffmpeg-progress
|
|
3
|
-
Version: 5.1.0
|
|
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
|
-
<div align="center">
|
|
21
|
-
|
|
22
|
-
[](https://pypistats.org/packages/better-ffmpeg-progress)
|
|
23
|
-
[](https://pypistats.org/packages/better-ffmpeg-progress)
|
|
24
|
-

|
|
25
|
-
[](LICENSE.txt)
|
|
26
|
-
|
|
27
|
-
# Better FFmpeg Progress
|
|
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.
|
|
31
|
-
</div>
|
|
32
|
-
|
|
33
|
-
FFmpeg outputs something like:
|
|
34
|
-
```
|
|
35
|
-
frame= 692 fps= 58 q=28.0 size= 5376KiB time=00:00:28.77 bitrate=1530.3kbits/s speed=2.43x
|
|
36
|
-
```
|
|
37
|
-
Better FFmpeg Progress outputs something like:
|
|
38
|
-
```
|
|
39
|
-
⠏ Processing abc.webm ━━━━━━━━━╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 23% 0:00:04 00:15
|
|
40
|
-
```
|
|
41
|
-
Where:
|
|
42
|
-
- `Processing abc.webm` is the description of the progresss bar.
|
|
43
|
-
- `23%` is the percentage progress.
|
|
44
|
-
- `0:00:04` is the time elapsed.
|
|
45
|
-
- `00:15` is the estimated time until the FFmpeg process completes.
|
|
46
|
-
|
|
47
|
-
As you can see, the output of Better FFmpeg Progress is much more useful.
|
|
48
|
-
|
|
49
|
-
## Installation
|
|
50
|
-
```
|
|
51
|
-
pip install better-ffmpeg-progress --upgrade
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## Usage
|
|
55
|
-
Create an instance of the `FfmpegProcess` class and supply a list of arguments like you would to `subprocess.run()` or `subprocess.Popen()`.
|
|
56
|
-
|
|
57
|
-
Example:
|
|
58
|
-
```py
|
|
59
|
-
from better_ffmpeg_progress import FfmpegProcess, FfmpegProcessError
|
|
60
|
-
|
|
61
|
-
command = [
|
|
62
|
-
"ffmpeg",
|
|
63
|
-
"-i",
|
|
64
|
-
"https://media.xiph.org/video/derf/y4m/ducks_take_off_1080p50.y4m",
|
|
65
|
-
"-map",
|
|
66
|
-
"0:V",
|
|
67
|
-
"-c:V",
|
|
68
|
-
"libx264",
|
|
69
|
-
"-preset",
|
|
70
|
-
"ultrafast",
|
|
71
|
-
"-f",
|
|
72
|
-
"null",
|
|
73
|
-
"-",
|
|
74
|
-
]
|
|
75
|
-
|
|
76
|
-
try:
|
|
77
|
-
process = FfmpegProcess(command)
|
|
78
|
-
# Uncomment the line below if you want to use tqdm instead of rich for the progress bar
|
|
79
|
-
# process.use_tqdm = True
|
|
80
|
-
|
|
81
|
-
process.run()
|
|
82
|
-
except FfmpegProcessError as e:
|
|
83
|
-
print(f"An error occurred when running the better-ffmpeg-process package:\n{e}")
|
|
84
|
-
```
|
|
85
|
-
## Optional Arguments
|
|
86
|
-
An instance of the `FfmpegProcess` class takes the following **optional** arguments:
|
|
87
|
-
|
|
88
|
-
- `ffmpeg_log_level` - Desired FFmpeg log level. Default: `"verbose"`
|
|
89
|
-
- `ffmpeg_log_file` - The filepath to save the FFmpeg log to. Default: `<input filename>_log.txt`
|
|
90
|
-
- `print_detected_duration` - Print the detected duration of the input file. Default: `False`
|
|
91
|
-
- `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`
|
|
92
|
-
|
|
93
|
-
The `run` method takes the following **optional** arguments:
|
|
94
|
-
- `print_command` - Print the FFmpeg command being executed. Default: `False`
|
|
File without changes
|
{better_ffmpeg_progress-5.1.0 → better_ffmpeg_progress-5.2.0}/better_ffmpeg_progress/__init__.py
RENAMED
|
File without changes
|
{better_ffmpeg_progress-5.1.0 → better_ffmpeg_progress-5.2.0}/better_ffmpeg_progress/enums.py
RENAMED
|
File without changes
|
{better_ffmpeg_progress-5.1.0 → better_ffmpeg_progress-5.2.0}/better_ffmpeg_progress/exceptions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|