ffmpeg-normalize 1.31.1__tar.gz → 1.31.3__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 (28) hide show
  1. {ffmpeg_normalize-1.31.1 → ffmpeg_normalize-1.31.3}/CHANGELOG.md +10 -0
  2. {ffmpeg_normalize-1.31.1 → ffmpeg_normalize-1.31.3}/PKG-INFO +11 -1
  3. {ffmpeg_normalize-1.31.1 → ffmpeg_normalize-1.31.3}/ffmpeg_normalize/__main__.py +1 -0
  4. {ffmpeg_normalize-1.31.1 → ffmpeg_normalize-1.31.3}/ffmpeg_normalize/_cmd_utils.py +1 -3
  5. {ffmpeg_normalize-1.31.1 → ffmpeg_normalize-1.31.3}/ffmpeg_normalize/_ffmpeg_normalize.py +3 -0
  6. {ffmpeg_normalize-1.31.1 → ffmpeg_normalize-1.31.3}/ffmpeg_normalize/_media_file.py +24 -12
  7. {ffmpeg_normalize-1.31.1 → ffmpeg_normalize-1.31.3}/ffmpeg_normalize/_streams.py +15 -14
  8. ffmpeg_normalize-1.31.3/ffmpeg_normalize/_version.py +1 -0
  9. {ffmpeg_normalize-1.31.1 → ffmpeg_normalize-1.31.3}/ffmpeg_normalize.egg-info/PKG-INFO +11 -1
  10. ffmpeg_normalize-1.31.1/ffmpeg_normalize/_version.py +0 -1
  11. {ffmpeg_normalize-1.31.1 → ffmpeg_normalize-1.31.3}/LICENSE +0 -0
  12. {ffmpeg_normalize-1.31.1 → ffmpeg_normalize-1.31.3}/README.md +0 -0
  13. {ffmpeg_normalize-1.31.1 → ffmpeg_normalize-1.31.3}/ffmpeg_normalize/__init__.py +0 -0
  14. {ffmpeg_normalize-1.31.1 → ffmpeg_normalize-1.31.3}/ffmpeg_normalize/_errors.py +0 -0
  15. {ffmpeg_normalize-1.31.1 → ffmpeg_normalize-1.31.3}/ffmpeg_normalize/_logger.py +0 -0
  16. {ffmpeg_normalize-1.31.1 → ffmpeg_normalize-1.31.3}/ffmpeg_normalize/py.typed +0 -0
  17. {ffmpeg_normalize-1.31.1 → ffmpeg_normalize-1.31.3}/ffmpeg_normalize.egg-info/SOURCES.txt +0 -0
  18. {ffmpeg_normalize-1.31.1 → ffmpeg_normalize-1.31.3}/ffmpeg_normalize.egg-info/dependency_links.txt +0 -0
  19. {ffmpeg_normalize-1.31.1 → ffmpeg_normalize-1.31.3}/ffmpeg_normalize.egg-info/entry_points.txt +0 -0
  20. {ffmpeg_normalize-1.31.1 → ffmpeg_normalize-1.31.3}/ffmpeg_normalize.egg-info/not-zip-safe +0 -0
  21. {ffmpeg_normalize-1.31.1 → ffmpeg_normalize-1.31.3}/ffmpeg_normalize.egg-info/requires.txt +0 -0
  22. {ffmpeg_normalize-1.31.1 → ffmpeg_normalize-1.31.3}/ffmpeg_normalize.egg-info/top_level.txt +0 -0
  23. {ffmpeg_normalize-1.31.1 → ffmpeg_normalize-1.31.3}/setup.cfg +0 -0
  24. {ffmpeg_normalize-1.31.1 → ffmpeg_normalize-1.31.3}/setup.py +0 -0
  25. {ffmpeg_normalize-1.31.1 → ffmpeg_normalize-1.31.3}/test/out.mp4 +0 -0
  26. {ffmpeg_normalize-1.31.1 → ffmpeg_normalize-1.31.3}/test/test.mp4 +0 -0
  27. {ffmpeg_normalize-1.31.1 → ffmpeg_normalize-1.31.3}/test/test.py +0 -0
  28. {ffmpeg_normalize-1.31.1 → ffmpeg_normalize-1.31.3}/test/test.wav +0 -0
@@ -1,6 +1,16 @@
1
1
  # Changelog
2
2
 
3
3
 
4
+ ## v1.31.3 (2025-04-14)
5
+
6
+ * Swap NUL to os.devnull.
7
+
8
+
9
+ ## v1.31.2 (2025-03-19)
10
+
11
+ * Fix: special handling of /dev/null.
12
+
13
+
4
14
  ## v1.31.1 (2025-02-19)
5
15
 
6
16
  * Fix bitrate setting for libvorbis, fixes #277.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ffmpeg_normalize
3
- Version: 1.31.1
3
+ Version: 1.31.3
4
4
  Summary: Normalize audio via ffmpeg
5
5
  Home-page: https://github.com/slhck/ffmpeg-normalize
6
6
  Author: Werner Robitza
@@ -667,6 +667,16 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
667
667
  # Changelog
668
668
 
669
669
 
670
+ ## v1.31.3 (2025-04-14)
671
+
672
+ * Swap NUL to os.devnull.
673
+
674
+
675
+ ## v1.31.2 (2025-03-19)
676
+
677
+ * Fix: special handling of /dev/null.
678
+
679
+
670
680
  ## v1.31.1 (2025-02-19)
671
681
 
672
682
  * Fix bitrate setting for libvorbis, fixes #277.
@@ -559,6 +559,7 @@ def main() -> None:
559
559
  extra_input_options=extra_input_options,
560
560
  extra_output_options=extra_output_options,
561
561
  output_format=cli_args.output_format,
562
+ extension=cli_args.extension,
562
563
  dry_run=cli_args.dry_run,
563
564
  progress=cli_args.progress,
564
565
  )
@@ -5,9 +5,8 @@ import os
5
5
  import re
6
6
  import shlex
7
7
  import subprocess
8
- from platform import system
9
8
  from shutil import which
10
- from typing import Iterator, Any
9
+ from typing import Any, Iterator
11
10
 
12
11
  from ffmpeg_progress_yield import FfmpegProgress
13
12
 
@@ -15,7 +14,6 @@ from ._errors import FFmpegNormalizeError
15
14
 
16
15
  _logger = logging.getLogger(__name__)
17
16
 
18
- NUL = "NUL" if system() in ("Windows", "cli") else "/dev/null"
19
17
  DUR_REGEX = re.compile(
20
18
  r"Duration: (?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})\.(?P<ms>\d{2})"
21
19
  )
@@ -79,6 +79,7 @@ class FFmpegNormalize:
79
79
  extra_input_options (list, optional): Extra input options. Defaults to None.
80
80
  extra_output_options (list, optional): Extra output options. Defaults to None.
81
81
  output_format (str, optional): Output format. Defaults to None.
82
+ extension (str, optional): Output file extension to use for output files that were not explicitly specified. Defaults to "mkv".
82
83
  dry_run (bool, optional): Dry run. Defaults to False.
83
84
  debug (bool, optional): Debug. Defaults to False.
84
85
  progress (bool, optional): Progress. Defaults to False.
@@ -117,6 +118,7 @@ class FFmpegNormalize:
117
118
  extra_input_options: list[str] | None = None,
118
119
  extra_output_options: list[str] | None = None,
119
120
  output_format: str | None = None,
121
+ extension: str = "mkv",
120
122
  dry_run: bool = False,
121
123
  debug: bool = False,
122
124
  progress: bool = False,
@@ -197,6 +199,7 @@ class FFmpegNormalize:
197
199
  self.post_filter = post_filter
198
200
 
199
201
  self.output_format = output_format
202
+ self.extension = extension
200
203
  self.dry_run = dry_run
201
204
  self.debug = debug
202
205
  self.progress = progress
@@ -10,7 +10,7 @@ from typing import TYPE_CHECKING, Iterable, Iterator, Literal, TypedDict
10
10
 
11
11
  from tqdm import tqdm
12
12
 
13
- from ._cmd_utils import DUR_REGEX, NUL, CommandRunner
13
+ from ._cmd_utils import DUR_REGEX, CommandRunner
14
14
  from ._errors import FFmpegNormalizeError
15
15
  from ._streams import (
16
16
  AudioStream,
@@ -67,7 +67,12 @@ class MediaFile:
67
67
  self.skip = False
68
68
  self.input_file = input_file
69
69
  self.output_file = output_file
70
- self.output_ext = os.path.splitext(output_file)[1][1:]
70
+ current_ext = os.path.splitext(output_file)[1][1:]
71
+ # we need to check if it's empty, e.g. /dev/null or NUL
72
+ if current_ext == "" or self.output_file == os.devnull:
73
+ self.output_ext = self.ffmpeg_normalize.extension
74
+ else:
75
+ self.output_ext = current_ext
71
76
  self.streams: StreamDict = {"audio": {}, "video": {}, "subtitle": {}}
72
77
 
73
78
  self.parse_streams()
@@ -109,7 +114,7 @@ class MediaFile:
109
114
  "0",
110
115
  "-f",
111
116
  "null",
112
- NUL,
117
+ os.devnull,
113
118
  ]
114
119
 
115
120
  output = CommandRunner().run_command(cmd).get_output()
@@ -424,13 +429,18 @@ class MediaFile:
424
429
  # if dry run, only show sample command
425
430
  if self.ffmpeg_normalize.dry_run:
426
431
  cmd.append(self.output_file)
432
+ _logger.warning("Dry run used, not actually running second-pass command")
427
433
  CommandRunner(dry=True).run_command(cmd)
428
434
  yield 100
429
435
  return
430
436
 
431
- temp_dir = mkdtemp()
432
- temp_file = os.path.join(temp_dir, f"out.{self.output_ext}")
433
- cmd.append(temp_file)
437
+ # special case: if output is a null device, write directly to it
438
+ if self.output_file == os.devnull:
439
+ cmd.append(self.output_file)
440
+ else:
441
+ temp_dir = mkdtemp()
442
+ temp_file = os.path.join(temp_dir, f"out.{self.output_ext}")
443
+ cmd.append(temp_file)
434
444
 
435
445
  cmd_runner = CommandRunner()
436
446
  try:
@@ -442,13 +452,15 @@ class MediaFile:
442
452
  )
443
453
  raise e
444
454
  else:
445
- _logger.debug(
446
- f"Moving temporary file from {temp_file} to {self.output_file}"
447
- )
448
- move(temp_file, self.output_file)
449
- rmtree(temp_dir, ignore_errors=True)
455
+ if self.output_file != os.devnull:
456
+ _logger.debug(
457
+ f"Moving temporary file from {temp_file} to {self.output_file}"
458
+ )
459
+ move(temp_file, self.output_file)
460
+ rmtree(temp_dir, ignore_errors=True)
450
461
  except Exception as e:
451
- rmtree(temp_dir, ignore_errors=True)
462
+ if self.output_file != os.devnull:
463
+ rmtree(temp_dir, ignore_errors=True)
452
464
  raise e
453
465
 
454
466
  output = cmd_runner.get_output()
@@ -4,9 +4,9 @@ import json
4
4
  import logging
5
5
  import os
6
6
  import re
7
- from typing import TYPE_CHECKING, Iterator, List, Literal, Optional, TypedDict, cast
7
+ from typing import TYPE_CHECKING, Iterator, Literal, TypedDict, cast
8
8
 
9
- from ._cmd_utils import NUL, CommandRunner, dict_to_filter_opts
9
+ from ._cmd_utils import CommandRunner, dict_to_filter_opts
10
10
  from ._errors import FFmpegNormalizeError
11
11
 
12
12
  if TYPE_CHECKING:
@@ -17,6 +17,7 @@ _logger = logging.getLogger(__name__)
17
17
 
18
18
  _loudnorm_pattern = re.compile(r"\[Parsed_loudnorm_(\d+)")
19
19
 
20
+
20
21
  class EbuLoudnessStatistics(TypedDict):
21
22
  input_i: float
22
23
  input_tp: float
@@ -239,7 +240,7 @@ class AudioStream(MediaStream):
239
240
  "-sn",
240
241
  "-f",
241
242
  "null",
242
- NUL,
243
+ os.devnull,
243
244
  ]
244
245
 
245
246
  cmd_runner = CommandRunner()
@@ -310,7 +311,7 @@ class AudioStream(MediaStream):
310
311
  "-sn",
311
312
  "-f",
312
313
  "null",
313
- NUL,
314
+ os.devnull,
314
315
  ]
315
316
 
316
317
  cmd_runner = CommandRunner()
@@ -322,11 +323,13 @@ class AudioStream(MediaStream):
322
323
  )
323
324
 
324
325
  # only one stream
325
- self.loudness_statistics["ebu_pass1"] = next(iter(AudioStream.prune_and_parse_loudnorm_output(output).values()))
326
+ self.loudness_statistics["ebu_pass1"] = next(
327
+ iter(AudioStream.prune_and_parse_loudnorm_output(output).values())
328
+ )
326
329
 
327
330
  @staticmethod
328
331
  def prune_and_parse_loudnorm_output(
329
- output: str
332
+ output: str,
330
333
  ) -> dict[int, EbuLoudnessStatistics]:
331
334
  """
332
335
  Prune ffmpeg progress lines from output and parse the loudnorm filter output.
@@ -344,7 +347,7 @@ class AudioStream(MediaStream):
344
347
 
345
348
  @staticmethod
346
349
  def _parse_loudnorm_output(
347
- output_lines: list[str]
350
+ output_lines: list[str],
348
351
  ) -> dict[int, EbuLoudnessStatistics]:
349
352
  """
350
353
  Parse the output of a loudnorm filter to get the EBU loudness statistics.
@@ -403,7 +406,9 @@ class AudioStream(MediaStream):
403
406
  # convert to floats
404
407
  loudnorm_stats[key] = float(loudnorm_stats[key])
405
408
 
406
- result[stream_index] = cast(EbuLoudnessStatistics, loudnorm_stats)
409
+ result[stream_index] = cast(
410
+ EbuLoudnessStatistics, loudnorm_stats
411
+ )
407
412
  stream_index = -1
408
413
  except Exception as e:
409
414
  raise FFmpegNormalizeError(
@@ -504,15 +509,11 @@ class AudioStream(MediaStream):
504
509
  "offset": self._constrain(
505
510
  stats["target_offset"], -99, 99, name="target_offset"
506
511
  ),
507
- "measured_i": self._constrain(
508
- stats["input_i"], -99, 0, name="input_i"
509
- ),
512
+ "measured_i": self._constrain(stats["input_i"], -99, 0, name="input_i"),
510
513
  "measured_lra": self._constrain(
511
514
  stats["input_lra"], 0, 99, name="input_lra"
512
515
  ),
513
- "measured_tp": self._constrain(
514
- stats["input_tp"], -99, 99, name="input_tp"
515
- ),
516
+ "measured_tp": self._constrain(stats["input_tp"], -99, 99, name="input_tp"),
516
517
  "measured_thresh": self._constrain(
517
518
  stats["input_thresh"], -99, 0, name="input_thresh"
518
519
  ),
@@ -0,0 +1 @@
1
+ __version__ = "1.31.3"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ffmpeg-normalize
3
- Version: 1.31.1
3
+ Version: 1.31.3
4
4
  Summary: Normalize audio via ffmpeg
5
5
  Home-page: https://github.com/slhck/ffmpeg-normalize
6
6
  Author: Werner Robitza
@@ -667,6 +667,16 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
667
667
  # Changelog
668
668
 
669
669
 
670
+ ## v1.31.3 (2025-04-14)
671
+
672
+ * Swap NUL to os.devnull.
673
+
674
+
675
+ ## v1.31.2 (2025-03-19)
676
+
677
+ * Fix: special handling of /dev/null.
678
+
679
+
670
680
  ## v1.31.1 (2025-02-19)
671
681
 
672
682
  * Fix bitrate setting for libvorbis, fixes #277.
@@ -1 +0,0 @@
1
- __version__ = "1.31.1"