better-ffmpeg-progress 3.2.1__tar.gz → 3.2.2__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.2.1
3
+ Version: 3.2.2
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
@@ -5,8 +5,6 @@ import psutil
5
5
  from queue import Queue
6
6
  import shutil
7
7
  import subprocess
8
- from threading import Thread
9
- from time import sleep
10
8
  from typing import List, Optional, Union
11
9
 
12
10
  from ffmpeg import probe
@@ -35,24 +33,6 @@ class FfmpegLogLevel(Enum):
35
33
 
36
34
 
37
35
  class FfmpegProcess:
38
- @staticmethod
39
- def _read_pipe(pipe: subprocess.PIPE, queue: Queue, stdout: bool = False) -> None:
40
- """Read from pipe and put lines into queue."""
41
- try:
42
- with pipe:
43
- for line in iter(pipe.readline, ""):
44
- line = line.strip()
45
- if line:
46
- if stdout:
47
- if line.startswith("out_time_ms="):
48
- queue.put(line)
49
- else:
50
- queue.put(line)
51
- except (IOError, ValueError) as e:
52
- print(f"Error reading pipe: {e}")
53
- finally:
54
- pipe.close()
55
-
56
36
  @classmethod
57
37
  def _validate_command(cls, command: List[str]) -> bool:
58
38
  if not shutil.which("ffmpeg"):
@@ -112,9 +92,6 @@ class FfmpegProcess:
112
92
  self._print_detected_duration = print_detected_duration
113
93
  self._print_stderr_new_line = print_stderr_new_line
114
94
 
115
- with open(self._ffmpeg_log_file, "w"):
116
- pass
117
-
118
95
  try:
119
96
  probe_data = probe(self._input_filepath)
120
97
  self._duration_secs = float(probe_data["format"]["duration"])
@@ -136,18 +113,7 @@ class FfmpegProcess:
136
113
 
137
114
  self.return_code = 0
138
115
 
139
- def _process_stderr(self, stderr: str) -> None:
140
- if self._duration_secs is None:
141
- print(stderr, end="\n" if self._print_stderr_new_line else "\r", flush=True)
142
-
143
- try:
144
- with open(self._ffmpeg_log_file, "a", encoding="utf-8") as f:
145
- f.write(f"{stderr}\n")
146
- except IOError as e:
147
- print(f"Error writing to log file: {e}")
148
- self.return_code = 1
149
-
150
- def _use_rich(self, process, stdout_queue, stderr_queue) -> int:
116
+ def _use_rich(self, process) -> int:
151
117
  with Progress(
152
118
  SpinnerColumn(),
153
119
  TextColumn("[progress.description]{task.description}"),
@@ -165,37 +131,32 @@ class FfmpegProcess:
165
131
  total=self._duration_secs,
166
132
  )
167
133
 
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()
134
+ if task_id is not None:
135
+ while process.poll() is None:
136
+ stdout = process.stdout.readline()
137
+
138
+ if not stdout.startswith("out_time_ms"):
139
+ continue
140
+
174
141
  try:
175
- value = int(line.split("=")[1]) / 1_000_000
142
+ value = int(stdout.split("=")[1]) / 1_000_000
143
+ except ValueError:
144
+ continue
145
+ else:
176
146
  if value <= self._duration_secs:
177
147
  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
148
 
196
- # Process remaining stderr
197
- while not stderr_queue.empty():
198
- self._process_stderr(stderr_queue.get())
149
+ if process.returncode == 0:
150
+ progress_bar.update(task_id, completed=self._duration_secs)
151
+ progress_bar.columns = (
152
+ TextColumn("[progress.description]{task.description}"),
153
+ BarColumn(),
154
+ TaskProgressColumn(),
155
+ )
156
+ progress_bar.update(
157
+ task_id,
158
+ description=f"✓ Processed {self._input_filepath.name}",
159
+ )
199
160
 
200
161
  if process.returncode != 0:
201
162
  progress_bar.update(
@@ -206,12 +167,12 @@ class FfmpegProcess:
206
167
 
207
168
  return 0
208
169
 
209
- def _use_tqdm(self, process, stdout_queue, stderr_queue) -> int:
210
- pbar = None
170
+ def _use_tqdm(self, process) -> int:
171
+ progress_bar = None
211
172
  width, _ = os.get_terminal_size()
212
173
 
213
174
  if self._duration_secs:
214
- pbar = tqdm(
175
+ progress_bar = tqdm(
215
176
  total=self._duration_secs,
216
177
  desc=f"Processing {self._input_filepath.name}",
217
178
  ncols=80,
@@ -219,33 +180,27 @@ class FfmpegProcess:
219
180
  bar_format="{desc} {bar} {percentage:.1f}% {elapsed} {remaining}",
220
181
  )
221
182
 
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()
183
+ if progress_bar is not None:
184
+ while process.poll() is None:
185
+ stdout = process.stdout.readline()
186
+
187
+ if not stdout.startswith("out_time_ms"):
188
+ continue
189
+
228
190
  try:
229
- value = int(line.split("=")[1]) / 1_000_000
230
- if value <= self._duration_secs:
231
- pbar.n = value
232
- pbar.refresh()
191
+ value = int(stdout.split("=")[1]) / 1_000_000
233
192
  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}")
193
+ continue
194
+ else:
195
+ if value <= self._duration_secs:
196
+ progress_bar.n = value
197
+ progress_bar.refresh()
242
198
 
243
- if pbar:
244
- pbar.close()
199
+ if process.returncode == 0:
200
+ progress_bar.n = self._duration_secs
201
+ progress_bar.set_description(f"✓ Processed {self._input_filepath.name}")
245
202
 
246
- # Process remaining stderr
247
- while not stderr_queue.empty():
248
- self._process_stderr(stderr_queue.get())
203
+ progress_bar.close()
249
204
 
250
205
  if process.returncode != 0:
251
206
  print(
@@ -288,38 +243,26 @@ class FfmpegProcess:
288
243
  self._ffmpeg_command = " ".join(self._ffmpeg_command)
289
244
 
290
245
  try:
291
- process = subprocess.Popen(
292
- self._ffmpeg_command,
293
- shell=isinstance(self._ffmpeg_command, str),
294
- stdout=subprocess.PIPE,
295
- stderr=subprocess.PIPE,
296
- text=True,
297
- creationflags=subprocess.CREATE_NEW_PROCESS_GROUP
298
- if os.name == "nt"
299
- else 0,
300
- )
246
+ with open(self._ffmpeg_log_file, "w") as f:
247
+ process = subprocess.Popen(
248
+ self._ffmpeg_command,
249
+ shell=isinstance(self._ffmpeg_command, str),
250
+ stdout=subprocess.PIPE,
251
+ stderr=f,
252
+ text=True,
253
+ creationflags=subprocess.CREATE_NEW_PROCESS_GROUP
254
+ if os.name == "nt"
255
+ else 0,
256
+ )
301
257
  except Exception as e:
302
258
  print(f"Error starting FFmpeg process: {e}")
303
259
  return 1
304
260
 
305
- stdout_queue, stderr_queue = Queue(), Queue()
306
- # Start stdout and stderr pipe readers
307
- Thread(
308
- target=self._read_pipe,
309
- args=(process.stdout, stdout_queue, True),
310
- daemon=True,
311
- ).start()
312
- Thread(
313
- target=self._read_pipe,
314
- args=(process.stderr, stderr_queue, False),
315
- daemon=True,
316
- ).start()
317
-
318
261
  try:
319
262
  if use_tqdm:
320
- return self._use_tqdm(process, stdout_queue, stderr_queue)
263
+ return self._use_tqdm(process)
321
264
  else:
322
- return self._use_rich(process, stdout_queue, stderr_queue)
265
+ return self._use_rich(process)
323
266
  except KeyboardInterrupt:
324
267
  try:
325
268
  psutil.Process(process.pid).terminate()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: better-ffmpeg-progress
3
- Version: 3.2.1
3
+ Version: 3.2.2
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
@@ -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.2.1",
8
+ version="3.2.2",
9
9
  author="GitHub.com/CrypticSignal",
10
10
  author_email="theaudiophile@outlook.com",
11
11
  description="Run FFmpeg & see percentage progress + ETA.",