better-ffmpeg-progress 5.3.0__tar.gz → 5.4.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 (17) hide show
  1. {better_ffmpeg_progress-5.3.0 → better_ffmpeg_progress-5.4.1}/PKG-INFO +24 -1
  2. {better_ffmpeg_progress-5.3.0 → better_ffmpeg_progress-5.4.1}/README.md +23 -0
  3. {better_ffmpeg_progress-5.3.0 → better_ffmpeg_progress-5.4.1}/better_ffmpeg_progress/better_ffmpeg_progress.py +14 -22
  4. {better_ffmpeg_progress-5.3.0 → better_ffmpeg_progress-5.4.1}/better_ffmpeg_progress/utils.py +12 -12
  5. {better_ffmpeg_progress-5.3.0 → better_ffmpeg_progress-5.4.1}/better_ffmpeg_progress.egg-info/PKG-INFO +24 -1
  6. {better_ffmpeg_progress-5.3.0 → better_ffmpeg_progress-5.4.1}/setup.py +1 -1
  7. {better_ffmpeg_progress-5.3.0 → better_ffmpeg_progress-5.4.1}/LICENSE +0 -0
  8. {better_ffmpeg_progress-5.3.0 → better_ffmpeg_progress-5.4.1}/better_ffmpeg_progress/__init__.py +0 -0
  9. {better_ffmpeg_progress-5.3.0 → better_ffmpeg_progress-5.4.1}/better_ffmpeg_progress/enums.py +0 -0
  10. {better_ffmpeg_progress-5.3.0 → better_ffmpeg_progress-5.4.1}/better_ffmpeg_progress/exceptions.py +0 -0
  11. {better_ffmpeg_progress-5.3.0 → better_ffmpeg_progress-5.4.1}/better_ffmpeg_progress/progress_bars.py +0 -0
  12. {better_ffmpeg_progress-5.3.0 → better_ffmpeg_progress-5.4.1}/better_ffmpeg_progress/terminate_process.py +0 -0
  13. {better_ffmpeg_progress-5.3.0 → better_ffmpeg_progress-5.4.1}/better_ffmpeg_progress.egg-info/SOURCES.txt +0 -0
  14. {better_ffmpeg_progress-5.3.0 → better_ffmpeg_progress-5.4.1}/better_ffmpeg_progress.egg-info/dependency_links.txt +0 -0
  15. {better_ffmpeg_progress-5.3.0 → better_ffmpeg_progress-5.4.1}/better_ffmpeg_progress.egg-info/requires.txt +0 -0
  16. {better_ffmpeg_progress-5.3.0 → better_ffmpeg_progress-5.4.1}/better_ffmpeg_progress.egg-info/top_level.txt +0 -0
  17. {better_ffmpeg_progress-5.3.0 → better_ffmpeg_progress-5.4.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: better-ffmpeg-progress
3
- Version: 5.3.0
3
+ Version: 5.4.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
@@ -108,6 +108,29 @@ process = FfmpegProcess(
108
108
  process.run()
109
109
  ```
110
110
 
111
+ ## Generated (lavfi) Inputs
112
+ Inputs generated by FFmpeg's [lavfi](https://ffmpeg.org/ffmpeg-devices.html#lavfi) device, such as test patterns, are supported. FFprobe cannot detect the duration of these inputs, so provide it with `duration_override`:
113
+
114
+ ```python
115
+ from better_ffmpeg_progress import FfmpegProcess
116
+
117
+ command = [
118
+ "ffmpeg",
119
+ "-f",
120
+ "lavfi",
121
+ "-i",
122
+ "testsrc2=duration=30:size=1920x1080:rate=30",
123
+ "-c:v",
124
+ "libx264",
125
+ "output.mp4",
126
+ ]
127
+
128
+ process = FfmpegProcess(command, duration_override=30)
129
+ process.run()
130
+ ```
131
+
132
+ By default, the FFmpeg log file is named after the source, e.g. `lavfi_testsrc2_ffmpeg_log.txt`.
133
+
111
134
  ## Optional Arguments
112
135
  An instance of `FfmpegProcess` accepts the following optional arguments:
113
136
  * `ffmpeg_log_level` — Set a value for FFmpeg's `-loglevel` option. Default: `"verbose"`.
@@ -89,6 +89,29 @@ process = FfmpegProcess(
89
89
  process.run()
90
90
  ```
91
91
 
92
+ ## Generated (lavfi) Inputs
93
+ Inputs generated by FFmpeg's [lavfi](https://ffmpeg.org/ffmpeg-devices.html#lavfi) device, such as test patterns, are supported. FFprobe cannot detect the duration of these inputs, so provide it with `duration_override`:
94
+
95
+ ```python
96
+ from better_ffmpeg_progress import FfmpegProcess
97
+
98
+ command = [
99
+ "ffmpeg",
100
+ "-f",
101
+ "lavfi",
102
+ "-i",
103
+ "testsrc2=duration=30:size=1920x1080:rate=30",
104
+ "-c:v",
105
+ "libx264",
106
+ "output.mp4",
107
+ ]
108
+
109
+ process = FfmpegProcess(command, duration_override=30)
110
+ process.run()
111
+ ```
112
+
113
+ By default, the FFmpeg log file is named after the source, e.g. `lavfi_testsrc2_ffmpeg_log.txt`.
114
+
92
115
  ## Optional Arguments
93
116
  An instance of `FfmpegProcess` accepts the following optional arguments:
94
117
  * `ffmpeg_log_level` — Set a value for FFmpeg's `-loglevel` option. Default: `"verbose"`.
@@ -1,5 +1,7 @@
1
1
  import math
2
2
  import os
3
+ import re
4
+ import shlex
3
5
  from pathlib import Path
4
6
  import subprocess
5
7
  from sys import exit
@@ -16,7 +18,7 @@ from .utils import (
16
18
  validate_ffmpeg_command,
17
19
  get_media_duration,
18
20
  get_expected_output_duration,
19
- check_shell_needed_for_command,
21
+ is_lavfi_input,
20
22
  )
21
23
  from .terminate_process import terminate_ffmpeg_process
22
24
  from .progress_bars import use_rich, use_tqdm
@@ -112,7 +114,14 @@ class FfmpegProcess:
112
114
  self._output_filepath = Path(command[-1])
113
115
 
114
116
  if ffmpeg_log_file is None:
115
- self._ffmpeg_log_file = Path(f"{self._input_filepath.name}_ffmpeg_log.txt")
117
+ if is_lavfi_input(command, input_file_index):
118
+ # A filtergraph such as "testsrc2=size=1920x1080" can contain characters that
119
+ # are not valid in filenames (e.g. ":" on Windows), so name the log after the
120
+ # source instead.
121
+ source_name = re.sub(r"[^\w.,-]", "_", input_file_path_str.split("=", 1)[0])
122
+ self._ffmpeg_log_file = Path(f"lavfi_{source_name}_ffmpeg_log.txt")
123
+ else:
124
+ self._ffmpeg_log_file = Path(f"{self._input_filepath.name}_ffmpeg_log.txt")
116
125
  elif isinstance(ffmpeg_log_file, (str, os.PathLike)):
117
126
  self._ffmpeg_log_file = Path(ffmpeg_log_file)
118
127
  else:
@@ -182,22 +191,7 @@ class FfmpegProcess:
182
191
  print_command: bool = False,
183
192
  ) -> None:
184
193
  if print_command:
185
- cmd_str = (
186
- " ".join(self._ffmpeg_command)
187
- if isinstance(self._ffmpeg_command, list)
188
- else self._ffmpeg_command
189
- )
190
- print(f"Executing: {cmd_str}")
191
-
192
- self._shell_needed = check_shell_needed_for_command(
193
- self._ffmpeg_command
194
- if isinstance(self._ffmpeg_command, list)
195
- else [self._ffmpeg_command]
196
- )
197
-
198
- current_ffmpeg_command = self._ffmpeg_command
199
- if self._shell_needed and isinstance(current_ffmpeg_command, list):
200
- current_ffmpeg_command = " ".join(current_ffmpeg_command)
194
+ print(f"Executing: {shlex.join(self._ffmpeg_command)}")
201
195
 
202
196
  try:
203
197
  creationflags = 0
@@ -212,16 +206,14 @@ class FfmpegProcess:
212
206
  encoding="utf-8",
213
207
  ) as f:
214
208
  self._process = subprocess.Popen(
215
- current_ffmpeg_command,
216
- shell=self._shell_needed,
209
+ self._ffmpeg_command,
217
210
  stdout=subprocess.PIPE,
218
211
  stderr=f,
219
212
  creationflags=creationflags,
220
213
  )
221
214
  else:
222
215
  self._process = subprocess.Popen(
223
- current_ffmpeg_command,
224
- shell=self._shell_needed,
216
+ self._ffmpeg_command,
225
217
  stdout=subprocess.PIPE,
226
218
  stderr=self._ffmpeg_log_file,
227
219
  creationflags=creationflags,
@@ -15,6 +15,14 @@ _BYTES_PER_MB = 1024 * 1024
15
15
  _MULTIPLIER = 1e-6
16
16
 
17
17
 
18
+ def is_lavfi_input(command: List[str], input_flag_pos: int) -> bool:
19
+ """
20
+ Returns True if the input at input_flag_pos is preceded by "-f lavfi", meaning it is a
21
+ filtergraph (e.g. "testsrc2=duration=30") generated by FFmpeg rather than a file or URL.
22
+ """
23
+ return command[max(input_flag_pos - 2, 0) : input_flag_pos] == ["-f", "lavfi"]
24
+
25
+
18
26
  def validate_ffmpeg_command(command: List[str]) -> None:
19
27
  """
20
28
  Validates the FFmpeg command.
@@ -54,8 +62,10 @@ def validate_ffmpeg_command(command: List[str]) -> None:
54
62
  f"Input file path '{input_file}' looks like an option flag. Please check your command."
55
63
  )
56
64
 
57
- # Validate input file/URL
58
- if input_file.startswith("http://") or input_file.startswith("https://"):
65
+ # Validate input file/URL. lavfi inputs are generated by FFmpeg, so there is nothing to check.
66
+ if is_lavfi_input(command, input_flag_pos):
67
+ pass
68
+ elif input_file.startswith("http://") or input_file.startswith("https://"):
59
69
  try:
60
70
  response = requests.head(input_file, timeout=5, allow_redirects=True)
61
71
  response.raise_for_status()
@@ -107,16 +117,6 @@ def get_media_duration(input_file: str) -> Optional[float]:
107
117
  return None
108
118
 
109
119
 
110
- def check_shell_needed_for_command(command: List[str]) -> bool:
111
- """
112
- Checks if any part of the command contains shell operators,
113
- indicating that shell=True might be needed for subprocess.Popen.
114
- This is a basic check and might not cover all edge cases.
115
- """
116
- shell_operators = {"|", ">", "<", ">>", "&&", "||"} # Basic set
117
- return any(op in item for item in command for op in shell_operators)
118
-
119
-
120
120
  def parse_ffmpeg_progress_line(
121
121
  line: bytes, total_duration_secs: Optional[float]
122
122
  ) -> Optional[float]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: better-ffmpeg-progress
3
- Version: 5.3.0
3
+ Version: 5.4.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
@@ -108,6 +108,29 @@ process = FfmpegProcess(
108
108
  process.run()
109
109
  ```
110
110
 
111
+ ## Generated (lavfi) Inputs
112
+ Inputs generated by FFmpeg's [lavfi](https://ffmpeg.org/ffmpeg-devices.html#lavfi) device, such as test patterns, are supported. FFprobe cannot detect the duration of these inputs, so provide it with `duration_override`:
113
+
114
+ ```python
115
+ from better_ffmpeg_progress import FfmpegProcess
116
+
117
+ command = [
118
+ "ffmpeg",
119
+ "-f",
120
+ "lavfi",
121
+ "-i",
122
+ "testsrc2=duration=30:size=1920x1080:rate=30",
123
+ "-c:v",
124
+ "libx264",
125
+ "output.mp4",
126
+ ]
127
+
128
+ process = FfmpegProcess(command, duration_override=30)
129
+ process.run()
130
+ ```
131
+
132
+ By default, the FFmpeg log file is named after the source, e.g. `lavfi_testsrc2_ffmpeg_log.txt`.
133
+
111
134
  ## Optional Arguments
112
135
  An instance of `FfmpegProcess` accepts the following optional arguments:
113
136
  * `ffmpeg_log_level` — Set a value for FFmpeg's `-loglevel` option. Default: `"verbose"`.
@@ -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.3.0",
8
+ version="5.4.1",
9
9
  author="GitHub.com/CrypticSignal",
10
10
  author_email="theaudiophile@outlook.com",
11
11
  description="Run FFmpeg & see percentage progress + ETA.",