Giter VIP home page Giter VIP logo

better-ffmpeg-progress's Introduction

PyPI downloads PyPI downloads GitHub

Better FFmpeg Progress

Runs an FFmpeg command and uses tqdm to show a progress bar.

Example:

39%|███████████████████████████████████████████ | 23.6/60.2 [00:19<00:34, 1.07s/s]

Where:

  • 39% is the percentage progress.
  • 23.6 seconds of the input file have been processed.
  • 60.2 is the duration of the input file in seconds.
  • 00:19 is the time elapsed since the FFmpeg process started.
  • 00:34 is the estimated time required for the FFmpeg process to complete.
  • 1.07 shows how many seconds of the input file are processed per second.

Installation:

pip3 install better-ffmpeg-progress --upgrade

Usage:

Create an instance of the FfmpegProcess class and supply a list of arguments like you would to subprocess.run().

Simple Example:

from better_ffmpeg_progress import FfmpegProcess
# Pass a list of FFmpeg arguments, like you would if using subprocess.run()
process = FfmpegProcess(["ffmpeg", "-i", "input.mp4", "-c:a", "libmp3lame", "output.mp3"])
# Use the run method to run the FFmpeg command.
process.run()

Advanced Example:

from better_ffmpeg_progress import FfmpegProcess

def handle_progress_info(percentage, speed, eta, estimated_filesize):
    print(f"Estimated Output Filesize: {estimated_filesize / 1_000_000} MB")

def handle_success():
  # Code to run if the FFmpeg process completes successfully.
  pass

def handle_error():
  # Code to run if the FFmpeg process encounters an error.
  pass

# Pass a list of FFmpeg arguments, like you would if using subprocess.run()
process = FfmpegProcess(["ffmpeg", "-i", "input.mp4", "-c:a", "libmp3lame", "output.mp3"])

ffmpeg_output_path = 'ffmpeg_output.txt'

# Use the run method to run the FFmpeg command.
process.run(progress_handler=handle_progress_info, ffmpeg_output_file=ffmpeg_output_path, success_handler=handle_success, error_handler=handle_error)

The run method takes the following optional arguments:

  • progress_handler

    • You can create a function if you would like to do something with the following values:

      • Percentage progress. [float]
      • Speed, e.g. 22.3x which means that 22.3 seconds of the input are processed every second. [string]
      • ETA in seconds. [float]
      • Estimated output filesize in bytes. [float]
        • Note: This is not accurate. Please take the value with a grain of salt.

      The values will be None if unknown. The function will receive the aforementioned metrics as arguments, about two times per second.

  • ffmpeg_output_file - A string path to define where you want the output of FFmpeg to be saved. By default, this is saved in a folder named "ffmpeg_output", with the filename [<input_filename>].txt.

  • success_handler - A function to run if the FFmpeg process completes successfully.

  • error_handler - A function to run if the FFmpeg process encounters an error.

Changelog:

[19/09/2022]

  • Add the ability to specify a success_handler argument, a function to run if the FFmpeg process completes successfully.
  • Add 0.001 to tqdm's total parameter to prevent the chance of getting TqdmWarning: clamping frac to range [0, 1]

[21/12/2022]

  • [v2.0.7] Fix 'estimated_size' referenced before assignment error.
  • [v2.0.7] The progress bar now uses 1 decimal place for seconds processed and total duration.

[22/12/2022]

  • [v2.0.8] Add the ability to specify an error_handler argument, a function to run if the FFmpeg process encounters an error.
  • [v2.0.9] Fix a typo (commit da45a84)

[07/02/2022]

[05/11/2023]

[22/04/2024]

  • [v2.1.3] Fix issue #20

[28/04/2024]

  • [v2.1.4] Fix issue #21

[02/05/2024]

  • [v2.1.5] Fix issue #23 and make an error message more specific. Here is the relevant commit.

better-ffmpeg-progress's People

Contributors

crypticsignal avatar elgill avatar honhaochen avatar mrpaular avatar semenchenkoartem avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

better-ffmpeg-progress's Issues

Add a callback event?

Adding an optional callback/event that passes the 3+ numbers (and maybe the process handle) when needed might be useful for those who want to customize the output to
. Add ANSI colors
. Put the info into a GUI
. Reword/translate something or represent it as ASCII art.
. Remove or re-arrange things

the command is inconvenient to use

I tried this very simple command
"ffmpeg -i input.mp4 output.mkv"
and the result is error : (-i) + 1 or something,
turn out, you must replace space with ", ",
if the command simple like above, I don't mind,
but if it is long like this :
ffmpeg -loglevel quiet -stats -y -hwaccel auto -i "z:\test video\input.mp4" -vf scale=-4:720 -c:v hevc_nvenc -preset slow "z:\test video\output.mp4"
it nightmares,
your job should be just adding the "progress bar",
no more no less

_should_overwrite函数逻辑是不是有问题

def _should_overwrite(self):
    dirname = os.path.dirname(self._output_filepath)
    self._dir_files = (
        [file for file in os.listdir(dirname)] if dirname else [file for file in os.listdir()]
    )

    if "-y" not in self._ffmpeg_args and self._output_filepath in self._dir_files:
        choice = input(f"{self._output_filepath} already exists. Overwrite? [Y/N]: ").lower()

        if choice != "y":
            print(
                "FFmpeg will not run as the output filename already exists, and you do not want it to be overwritten."
            )
            return False

        self._ffmpeg_args.insert(1, "-y")
        return True

以上是源码,有可能返回None

'estimated_size' referenced before assignment

from better_ffmpeg_progress import FfmpegProcess


def handle_progress_info(percentage, speed, eta, estimated_filesize):
    print(f"The FFmpeg process is {percentage}% complete. ETA is {eta} seconds.")


# Pass a list of FFmpeg arguments, like you would if using subprocess.run()
process = FfmpegProcess(["ffmpeg", "-i", "/home/freetalk/Opinie over Auschwitz vakantiekamp VRT.mp4", "-c:v", "libvpx-vp9", "-threads", "8", "/home/freetalk/Opinie.webm"])

# Use the run method to run the FFmpeg command.
process.run(progress_handler=handle_progress_info)

CLI output:

The duration of /home/freetalk/Opinie over Auschwitz vakantiekamp VRT.mp4 has been detected as 2561.302 seconds.
Running: ffmpeg -y -i /home/freetalk/Opinie over Auschwitz vakantiekamp VRT.mp4 -c:v libvpx-vp9 -threads 8 /home/freetalk/Opinie.webm -hide_banner -loglevel verbose -progress pipe:1 -nostats
  0%|                                                                                                                               | 0/2561.3030000000003 [00:00<?, ?s/s]
[Error] local variable 'estimated_size' referenced before assignment
Exiting Better FFmpeg Progress.

Exists file alert

The ffmpeg alert "File 'xxx' already exists. Overwrite? [y/N]" is hidden by the progress bar, but can still press y and pass it.

Maybe show this alert before the bar, or add a param like "exist_ok"?

Feature: Add estimated file size

I was able to add an estimated file size that seems to estimate okay after a few progress updates. It checks for changes because ffmpeg sporadically writes to disk and it's probably the most accurate when it writes.

                totalSize = 0;
                prevTotalSize = 0;
                etaSize = "unknown"
                                                if "total_size" in ffmpeg_output:
                                                        totalSize = int(ffmpeg_output.split("=")[1]);
                                                        if(percentage != "unknown" and percentage > 0):
                                                                if(totalSize != prevTotalSize):
                                                                        prevTotalSize = totalSize;
                                                                        etaSize = int(totalSize * (100 / percentage))
                                                                        if(etaSize > (1024*1024*1024)):
                                                                                etaSize = str(round(etaSize / (1024*1024*1024), 2)) + "G";
                                                                        elif(etaSize > (1024*1024)):
                                                                                etaSize = str(round(etaSize / (1024*1024), 2)) + "M";
                                                                        elif(etaSize > 1024):
                                                                                etaSize = str(round(etaSize / (1024), 2)) + "K";

                                                elif "out_time_ms" in ffmpeg_output:
                                                        microseconds = int(ffmpeg_output[12:])
                                                        secs = microseconds / 1_000_000
                                                        if file_duration is not None:
                                                                percentage = round((secs / file_duration) * 100, 1)

                                                elif "speed" in ffmpeg_output:
                                                        speed = ffmpeg_output.split("=")[1].strip()
                                                        speed = 0 if " " in speed or "N/A" in speed else float(speed[:-1])
                                                        if speed != 0:
                                                                eta = (file_duration - secs) / speed
                                                                seconds = round(eta % 60)
                                                                minutes = int(eta / 60)
                                                                hours = int(minutes / 60)
                                                                minutes = round(minutes % 60)
                                                                eta_string = f"{hours}:{minutes:02d}:{seconds:02d}"

                                                        if progress_handler is None:
                                                                print(f"Progress: {percentage}% | Speed: {speed}x | ETA: {eta_string} | Estimated Size: {etaSize}    ", end="\r")
                                                        else:
                                                                progress_handler(percentage, f"{speed}x", eta)

[Better FFmpeg Process] invalid literal for int() with base 10: 'N/A'

Version : 2.1.4

I get this error nearly on every single conversion and i think i know where is the problem ( my knowledge isn't that good )
the value variable is what causing this in line 81
seconds_processed = round(int(value) / 1_000_000, 1)
I printed The Value just before this line and that was the output :

Running: ffmpeg -i D:\Python_FFMPEG\Input\Sickflick - Copy (2).mp4 D:\Python_FFMPEG\Output\Sickflick - Copy (2).mp4 -hide_banner -loglevel verbose -progress pipe:1 -nostats

1%|██ | 0/10.3 [00:00<?, ?s/s]

Value : 3200000
31%|█████████████████████████████████████████████████████▍ | 3.2/10.3 [00:05<00:12, 1.74s/s]
Value : N/A
[Better FFmpeg Process] invalid literal for int() with base 10: 'N/A'

no progress view

from better_ffmpeg_progress import FfmpegProcess
def FfmpegProcess_on():
    # Pass a list of FFmpeg arguments, like you would if using subprocess.run()
    process = FfmpegProcess(
        [
            ".\\ffmpeg\\ffmpeg",
            "-y",
            "-i",
            "X:/UpupooResource/2000141996/科技感设计.mp4",
            "-r",
            "10",
            "-pix_fmt",
            "yuv420p",
            "-vcodec",
            "libx264",
            "-preset",
            "veryfast",
            "-profile:v",
            "baseline",
            "-crf",
            "23",
            "-acodec",
            "aac",
            "-b:a",
            "32k",
            "-strict",
            "-5",
            "c:\\1.mp4",
        ]
    )

    # Use the run method to run the FFmpeg command.
    process.run()


if __name__ == "__main__":
    FfmpegProcess_on()

result:
Running: .\ffmpeg\ffmpeg -y -i X:/UpupooResource/2000141996/科技感设计.mp4 -r 10 -pix_fmt yuv420p -vcodec libx264 -preset veryfast -profile:v baseline -crf 23 -acodec aac -b:a 32k -strict -5 c:\1.mp4 -hide_banner -loglevel verbose

PS C:\Users\Administrator\Desktop>

Error: UnboundLocalError: cannot access local variable 'progress_bar' where it is not associated with a value

bug description

  • Trying to transcode some videos
  • progress bar doesn't show up, my existing use of tqdm doesn't seem to be interfering.
  • The only weird thing I'm doing here is that I'm calling the full path of ffmpeg.exe since my computer won't add ffmpeg to the system path

steps to reproduce

  • running python 3.11 on a windows machine
  • run script. here's my script: (just replace the folder names with your own folder names)
  • hit control+c and I get the error below.

Script

import os
from tqdm import tqdm
from better_ffmpeg_progress import FfmpegProcess


def get_video_files(directory):
    video_extensions = ['.mp4', '.mkv', '.avi', '.mov', '.flv', '.wmv']
    video_files = []

    for foldername, subfolders, filenames in os.walk(directory):
        for filename in filenames:
            if any(filename.lower().endswith(ext) for ext in video_extensions):
                full_path = os.path.join(foldername, filename)
                video_files.append(full_path)

    # sort list
    video_files.sort()
    
    return video_files

def transcode_to_hevc(input_file, output_file):
    # List of FFmpeg arguments
    command = ["C:/Users/paula/ffmpeg-6.0-essentials_build/bin/ffmpeg.exe", 
               "-i", input_file, 
               "-c:v", "libx265", 
               output_file]
    
    # Create FfmpegProcess object
    process = FfmpegProcess(command)
    
    # Run FFmpeg command
    process.run()

if __name__ == "__main__":
    input_directory = 'E:/Novatek/Movie/'  # current directory, you can change this to any directory you want
    output_directory = 'D:/dash cam videos 2023-08-27/transcoded dashcam videos/'  # Ensure this directory exists or change the path
    
    video_list = get_video_files(input_directory)
    # print(video_list)
    
    files_to_transcode = video_list # Replace with your list of files

    for file in tqdm(files_to_transcode):
        print(file)
        output_file = output_directory + file.split('/')[-1].replace('.MP4', '_hevc.mp4')
        transcode_to_hevc(file, output_file)

Error

File "<python path>", line 123, in run
PS <working directory> &<python path> <script path>
  0%|                                                                                 | 0/1176 [00:00<?, ?it/s]E:/Novatek/Movie/2022_0831_122548_212.MP4
Running: C:/Users/paula/ffmpeg-6.0-essentials_build/bin/ffmpeg.exe -y -i E:/Novatek/Movie/2022_0831_122548_212.MP4 -c:v libx265 D:/dash cam videos 2023-08-27/transcoded dashcam videos/2022_0831_122548_212_hevc.mp4 -hide_banner -loglevel verbose
  0%|                                                                                 | 0/1176 [00:21<?, ?it/s]
Traceback (most recent call last):
  File "C:\Users\paula\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\better_ffmpeg_progress\better_ffmpeg_progress.py", line 66, in run
    while process.poll() is None:
          ^^^^^^^^^^^^^^
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.1264.0_x64__qbz5n2kfra8p0\Lib\subprocess.py", line 1236, in poll
    return self._internal_poll()
           ^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.1264.0_x64__qbz5n2kfra8p0\Lib\subprocess.py", line 1575, in _internal_poll
    if _WaitForSingleObject(self._handle, 0) == _WAIT_OBJECT_0:
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
KeyboardInterrupt

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "c:\Users\paula\python-learning\transcode-videos.py", line 47, in <module>
    transcode_to_hevc(file, output_file)
  File "c:\Users\paula\python-learning\transcode-videos.py", line 33, in transcode_to_hevc
    process.run()
  File "C:\Users\paula\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\better_ffmpeg_progress\better_ffmpeg_progress.py", line 117, in run
    progress_bar.close()
    ^^^^^^^^^^^^
UnboundLocalError: cannot access local variable 'progress_bar' where it is not associated with a value```

[BUG] local variable 'progress_bar' referenced before assignment

HI, after runnning the primary example with some minor modifications I got an exception.
I'm running version 2.0.5.

Code:

from better_ffmpeg_progress import FfmpegProcess
# Pass a list of FFmpeg arguments, like you would if using subprocess.run()
process = FfmpegProcess(["ffmpeg", "-i", "./music/arctic.flac", "-c:a", "libopus", "-b:a", "64k", "arctic.mka"])
# Use the run method to run the FFmpeg command.
process.run()

Exception:

Traceback (most recent call last):
  File "[REDACTED].py", line 5, in <module>
    process.run()
  File "[REDACTED]/.venv/lib/python3.10/site-packages/better_ffmpeg_progress/better_ffmpeg_progress.py", line 114, in run
    progress_bar.close()
UnboundLocalError: local variable 'progress_bar' referenced before assignment

No progress bar is displayed

Ubuntu 20.04.4 LTS
PyCharm 2023.3.4 (Community Edition)
Python 3.8.10

The duration of input.mp4 has been detected as 144.033 seconds.

from better_ffmpeg_progress import FfmpegProcess

# Pass a list of FFmpeg arguments, like you would if using subprocess.run()
process = FfmpegProcess(["ffmpeg", "-i", "input.mp4", "output.avi"])
# Use the run method to run the FFmpeg command.
process.run()

[Error] float division by zero

Hi,

when I run the script with the progress_handler I see the error message:

(env) [xxxx]$ /Users/[xxxx]/env/bin/python [xxxx]test_runner.py
Running: ffmpeg -y -i [xxxx] -c:a libmp3lame -y output.mp3 -loglevel verbose -progress pipe:1 -nostats
0%| | 0/384.304 [00:00<?, ?s/s]
[Error] float division by zero
Exiting Better FFmpeg Progress.

The code below:

from better_ffmpeg_progress import FfmpegProcess

def handle_progress_info(percentage):
print(f"The FFmpeg process is {percentage}% complete. ETA is / seconds.")

process = FfmpegProcess(["ffmpeg", "-i", "[xxxx]", "-c:a", "libmp3lame", "-y", "output.mp3"], )
process.run(progress_handler=handle_progress_info)

Command after its done will exit out the rest of the code

Hi,

I am using this script in more complex code and it looks like that after ffmpeg proccess is done , this command would stop further execution of the code...
It looks like " sys.exit() at the line 160 " is responsible for that kind of behaviour.

[FR] Support multiple input files and split options

Future Request Reason

Currently, the script just calls self._set_file_info() once, so we can only get one input file durations.

By the way, ffmpeg support spilt file by these options, we should support -ss, -t and -to, it is common to use.
image

Solution

I will work in #17

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.