Metadata-Version: 2.4
Name: better-ffmpeg-progress
Version: 6.0.0
Summary: Run FFmpeg & see percentage progress + ETA.
Author-email: "GitHub.com/CrypticSignal" <theaudiophile@outlook.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/CrypticSignal/better-ffmpeg-progress
Project-URL: Changelog, https://github.com/CrypticSignal/better-ffmpeg-progress/blob/main/CHANGELOG.md
Keywords: ffmpeg,progress
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: psutil
Requires-Dist: rich
Requires-Dist: tqdm
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Dynamic: license-file

<div align="center">

# Better FFmpeg Progress
Runs an FFmpeg command and shows a progress bar with percentage progress, elapsed time and ETA.
</div>

FFmpeg outputs something like:
```
frame=  692 fps= 58 q=28.0 size=    5376KiB time=00:00:28.77 bitrate=1530.3kbits/s speed=2.43x
```
Better FFmpeg Progress outputs something like:
```
⠏ Processing abc.webm (5.50 MB / ~23.91 MB) ━━━━━━━━━╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━  23% 0:00:04 00:15
```
Where:
- `Processing abc.webm` is the description of the progresss bar. Filenames longer than 60 characters are truncated.
- `(5.50 MB / ~23.91 MB)` is the current output size and the estimated final output size.
- `23%` is the percentage progress.
- `0:00:04` is the time elapsed.
- `00:15` is the estimated time until the FFmpeg process completes.

## Installation
```bash
pip install better-ffmpeg-progress --upgrade
```

## Usage
Create an instance of the `FfmpegProcess` class and supply a list of arguments like you would when using `subprocess.run()` or `subprocess.Popen()`. Example:

```python
from better_ffmpeg_progress import FfmpegProcess, FfmpegProcessError

command = [
    "ffmpeg",
    "-i",
    "https://media.xiph.org/video/derf/y4m/ducks_take_off_1080p50.y4m",
    "-map",
    "0:V",
    "-c:V",
    "libx264",
    "-preset",
    "ultrafast",
    "-f",
    "null",
    "-",
]

try:
    # Pass use_tqdm=True to use tqdm instead of Rich.
    process = FfmpegProcess(command)

    process.run()
except FfmpegProcessError as e:
    print(
        "An error occurred when running Better FFmpeg Progress:\n"
        f"{e}"
    )
```

## Trimming
If your command trims the input with `-ss`, `-sseof`, `-t` or `-to` (as input or output options), the expected duration of the output is calculated automatically, so the progress bar and ETA reflect the trimmed output rather than the whole input file. This adjustment is skipped when `-copyts` is used.

## Duration Override
By default, Better FFmpeg Progress detects the duration of the input file and uses it to calculate percentage progress and ETA.

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`.

The value should be the duration of the input file, in seconds:

```python
from better_ffmpeg_progress import FfmpegProcess

command = [
    "ffmpeg",
    "-i",
    "partial-input.flv",
    "-c",
    "copy",
    "recovered-output.mkv",
]

process = FfmpegProcess(
    command,
    duration_override=1222.64,
    print_detected_duration=True,
)

process.run()
```

## Generated (lavfi) Inputs
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`:

```python
from better_ffmpeg_progress import FfmpegProcess

command = [
    "ffmpeg",
    "-f",
    "lavfi",
    "-i",
    "testsrc2=duration=30:size=1920x1080:rate=30",
    "-c:v",
    "libx264",
    "output.mp4",
]

process = FfmpegProcess(command, duration_override=30)
process.run()
```

By default, the FFmpeg log file is named after the source, e.g. `lavfi_testsrc2_ffmpeg_log.txt`.

## Optional Arguments
An instance of `FfmpegProcess` accepts the following optional arguments:
* `ffmpeg_log_level` — Set a value for FFmpeg's `-loglevel` option. Default: `"verbose"`.
* `ffmpeg_log_file` — Filepath or file-like object to which FFmpeg stderr will be written. Default: `<input filename>_ffmpeg_log.txt`.
* `print_detected_duration` — Print the detected duration. Default: `False`.
* `duration_override` — Specify input file duration in seconds to use for progress calculation, instead of trying to detect this automatically with FFprobe. Default: `None`.
* `use_tqdm` — Use tqdm instead of Rich for the progress bar. Default: `False`.
* `overwrite` — What to do if the output file already exists: `True` overwrites it, `False` raises `FfmpegCommandError`, and `None` asks whether to overwrite it. Ignored if the command contains `-y` or `-n`. Default: `None`.

The `run` method accepts the following optional argument:
* `print_command` — Print the FFmpeg command being executed. Default: `False`.

## Using tqdm
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 passing `use_tqdm=True`:
```python
process = FfmpegProcess(command, use_tqdm=True)
process.run()
```
## Limitations
- Progress is calculated from the duration of the first input (the first `-i`).
- The last argument of the command is treated as the output file, e.g. when checking whether it already exists. With multiple outputs, only the last one is checked.

## Development
Install the package in editable mode with the test dependencies, then run the tests (FFmpeg and FFprobe must be installed):
```bash
pip install -e ".[test]"
```
```bash
pytest
```
`test.py` is a standalone example that encodes a test pattern and shows the progress bar.

## Publishing
Update `__version__` in `better_ffmpeg_progress/__init__.py` and add an entry to `CHANGELOG.md`.

One-time setup:
```bash
pip install --upgrade build twine pkginfo
```

Then, for each release:
```bash
rm -rf dist
```
```bash
python -m build
```
```bash
twine check dist/*
```
```bash
twine upload dist/*
```
