Goodbye tqdm (#1221)

* Goodbye tqdm

* Goodbye tqdm

* remove unused api

* show just file name

* support different mode

* support different mode

* minor polishing

* minor polishing

* switch to ProgressUnit to avoid confusion with ProcessMode

* improve naming
This commit is contained in:
Henry Ruhs
2026-08-21 17:40:35 +02:00
committed by GitHub
parent 0d20ce3e3a
commit 25bc9820f7
11 changed files with 176 additions and 39 deletions
+10 -1
View File
@@ -2,7 +2,7 @@ import logging
from typing import List, Sequence, get_args
from facefusion.common_helper import create_float_range, create_int_range
from facefusion.types import Angle, ApiSecurityStrategy, AudioEncoder, AudioFormat, AudioSet, BenchmarkMode, BenchmarkResolution, BenchmarkSet, DownloadProvider, DownloadProviderSet, DownloadScope, ExecutionProvider, ExecutionProviderSet, FaceAlignerModel, FaceDetectorModel, FaceDetectorSet, FaceMaskArea, FaceMaskAreaSet, FaceMaskRegion, FaceMaskRegionSet, FaceMaskType, FaceOccluderModel, FaceParserModel, FaceSelectorGender, FaceSelectorMode, FaceSelectorOrder, FaceSelectorRace, Gender, ImageEncoder, ImageFormat, ImageSet, JobStatus, LogLevel, LogLevelSet, Race, Score, TempFrameFormat, TempPixelFormat, VideoEncoder, VideoFormat, VideoMemoryStrategy, VideoPreset, VideoSet, VoiceExtractorModel, WorkflowMode, WorkflowStrategy
from facefusion.types import Angle, ApiSecurityStrategy, AudioEncoder, AudioFormat, AudioSet, BenchmarkMode, BenchmarkResolution, BenchmarkSet, DownloadProvider, DownloadProviderSet, DownloadScope, ExecutionProvider, ExecutionProviderSet, FaceAlignerModel, FaceDetectorModel, FaceDetectorSet, FaceMaskArea, FaceMaskAreaSet, FaceMaskRegion, FaceMaskRegionSet, FaceMaskType, FaceOccluderModel, FaceParserModel, FaceSelectorGender, FaceSelectorMode, FaceSelectorOrder, FaceSelectorRace, Gender, ImageEncoder, ImageFormat, ImageSet, JobStatus, LogLevel, LogLevelSet, ProgressActionSet, Race, Score, TempFrameFormat, TempPixelFormat, VideoEncoder, VideoFormat, VideoMemoryStrategy, VideoPreset, VideoSet, VoiceExtractorModel, WorkflowMode, WorkflowStrategy
face_detector_set : FaceDetectorSet =\
{
@@ -151,6 +151,15 @@ log_level_set : LogLevelSet =\
}
log_levels : List[LogLevel] = list(get_args(LogLevel))
progress_action_set : ProgressActionSet =\
{
'color_active': '\033[94m',
'color_neutral': '\033[90m',
'cursor_start': '\033[G',
'erase_line': '\033[K',
'reset': '\033[0m'
}
job_statuses : List[JobStatus] = list(get_args(JobStatus))
benchmark_cycle_count_range : Sequence[int] = create_int_range(1, 10, 1)
+127
View File
@@ -0,0 +1,127 @@
import os
import shutil
import sys
import time
from contextlib import contextmanager
from functools import partial
from types import SimpleNamespace
from typing import Iterator, Sized
from facefusion import choices
from facefusion.types import ProgressUnit
@contextmanager
def create(unit : ProgressUnit = 'frame', current : int = 0, total : int = 0) -> Iterator[SimpleNamespace]:
progress = SimpleNamespace(
unit = unit,
current = current,
total = total,
time_start = time.monotonic(),
time_update = 0.0
)
progress.count = partial(count, progress)
progress.set_title = partial(set_title, progress)
progress.set_description = partial(set_description, progress)
progress.update = partial(update, progress)
progress.seek = partial(seek, progress)
yield progress
progress.current = progress.total
render(progress)
sys.stdout.flush()
sys.stdout.write(os.linesep)
def set_title(progress : SimpleNamespace, title : str) -> None:
progress.title = title
def set_description(progress : SimpleNamespace, description : str) -> None:
progress.description = description
def count(progress : SimpleNamespace, collection : Sized) -> None:
progress.total = len(collection)
def update(progress : SimpleNamespace) -> None:
seek(progress, progress.current + 1)
def seek(progress : SimpleNamespace, current : int) -> None:
progress.current = current
time_current = time.monotonic()
if time_current - progress.time_update > 0.5:
progress.time_update = time_current
render(progress)
def render(progress : SimpleNamespace) -> None:
title = getattr(progress, 'title', '')
description = getattr(progress, 'description', '')
status = str(progress.current)
if progress.unit == 'percent':
status = resolve_percent(progress)
if progress.unit == 'frame':
status = resolve_frame(progress)
if progress.unit == 'download':
status = resolve_download(progress)
progress_width = shutil.get_terminal_size().columns - len(title) - len(status) - 2
if description:
progress_width -= len(description) + 3
if progress.total > 0:
progress_fill = progress.current * progress_width // progress.total
else:
progress_fill = 0
progress_bar = choices.progress_action_set.get('color_active') + '=' * progress_fill + choices.progress_action_set.get('color_neutral') + '=' * (progress_width - progress_fill) + choices.progress_action_set.get('reset')
progress_parts = [ title, progress_bar, status ]
if description:
progress_parts.append('|')
progress_parts.append(description)
sys.stdout.write(choices.progress_action_set.get('cursor_start') + ' '.join(progress_parts) + choices.progress_action_set.get('erase_line'))
sys.stdout.flush()
def resolve_percent(progress : SimpleNamespace) -> str:
if progress.total > 0:
percent = progress.current * 100 // progress.total
return str(percent) + ' %'
return '0 %'
def resolve_frame(progress : SimpleNamespace) -> str:
time_current = time.monotonic()
if time_current - progress.time_start > 0:
rate = progress.current / (time_current - progress.time_start)
return str(round(rate, 1)) + 'frame/s'
return '0.0frame/s'
def resolve_download(progress : SimpleNamespace) -> str:
time_current = time.monotonic()
if time_current - progress.time_start > 0:
rate = progress.current / (time_current - progress.time_start)
if rate > 1024 * 1024:
return str(round(rate / (1024 * 1024), 1)) + 'mb/s'
return str(round(rate / 1024, 1)) + 'kb/s'
return '0.0kb/s'
+5 -4
View File
@@ -2,9 +2,8 @@ from functools import lru_cache
from typing import Tuple
import numpy
from tqdm import tqdm
from facefusion import inference_manager, state_manager, translator, video_manager
from facefusion import cli_progress, inference_manager, translator, video_manager
from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url
from facefusion.filesystem import resolve_relative_path
from facefusion.thread_helper import conditional_thread_semaphore
@@ -166,7 +165,9 @@ def analyse_video(video_path : str, trim_frame_start : int, trim_frame_end : int
if trim_frame_start > 0:
video_manager.seek_video_reader(video_reader, trim_frame_start)
with tqdm(total = len(frame_range), desc = translator.get('analysing'), unit = 'frame', ascii = ' =', disable = state_manager.get_item('log_level') in [ 'warn', 'error' ]) as progress:
with cli_progress.create(unit = 'frame') as progress:
progress.set_title(translator.get('analysing'))
progress.count(frame_range)
for frame_number in frame_range:
vision_frame = video_manager.read_video_frame(video_reader)
@@ -181,7 +182,7 @@ def analyse_video(video_path : str, trim_frame_start : int, trim_frame_end : int
if counter > 0 and total > 0:
rate = counter / total * 100
progress.set_postfix(rate = rate)
progress.set_description('rate = ' + str(rate))
progress.update()
return bool(rate > 10.0)
+1 -2
View File
@@ -101,8 +101,7 @@ def pre_check() -> bool:
def common_pre_check() -> bool:
content_analyser_content = inspect.getsource(content_analyser).encode()
return hash_helper.create_hash(content_analyser_content) == '3c6ce25e'
return hash_helper.create_hash(content_analyser_content) == '61e33ef3'
def processors_pre_check() -> bool:
+6 -6
View File
@@ -4,10 +4,8 @@ from functools import lru_cache
from typing import List, Optional, Tuple
from urllib.parse import urlparse
from tqdm import tqdm
import facefusion.choices
from facefusion import curl_builder, logger, process_manager, state_manager, translator
from facefusion import cli_progress, curl_builder, logger, process_manager, state_manager, translator
from facefusion.filesystem import get_file_name, get_file_size, is_file, remove_file
from facefusion.hash_helper import validate_hash
from facefusion.types import Buffer, Command, DownloadProvider, DownloadSet
@@ -26,7 +24,10 @@ def conditional_download(download_directory_path : str, urls : List[str]) -> Non
download_size = get_static_download_size(url)
if initial_size < download_size:
with tqdm(total = download_size, initial = initial_size, desc = translator.get('downloading'), unit = 'B', unit_scale = True, unit_divisor = 1024, ascii = ' =', disable = state_manager.get_item('log_level') in [ 'warn', 'error' ]) as progress:
with cli_progress.create(unit = 'download', total = download_size) as progress:
progress.set_title(translator.get('downloading'))
progress.set_description('file_name = ' + download_file_name)
commands = curl_builder.chain(
curl_builder.download(url, download_file_path),
curl_builder.set_timeout(5),
@@ -34,12 +35,11 @@ def conditional_download(download_directory_path : str, urls : List[str]) -> Non
)
open_curl(commands)
current_size = initial_size
progress.set_postfix(download_providers = state_manager.get_item('download_providers'), file_name = download_file_name)
while current_size < download_size:
if is_file(download_file_path):
current_size = get_file_size(download_file_path)
progress.update(current_size - progress.n)
progress.seek(current_size)
@lru_cache(maxsize = 64)
+11 -14
View File
@@ -1,13 +1,11 @@
import os
import subprocess
import tempfile
from functools import lru_cache, partial
from functools import lru_cache
from typing import List, Optional, cast
from tqdm import tqdm
import facefusion.choices
from facefusion import ffmpeg_builder, ffprobe, logger, process_manager, state_manager, translator, vision
from facefusion import cli_progress, ffmpeg_builder, ffprobe, logger, process_manager, state_manager, translator, vision
from facefusion.filesystem import get_file_format, remove_file
from facefusion.temp_helper import get_temp_file_path, get_temp_frames_pattern
from facefusion.types import ApiSecurityStrategy, AudioEncoder, Buffer, Command, EncoderSet, Fps, Resolution, SampleRate, UpdateProgress, VideoEncoder, VideoFormat, VideoReaderMetadata
@@ -40,10 +38,6 @@ def run_ffmpeg_with_progress(commands : List[Command], update_progress : UpdateP
return process
def update_progress(progress : tqdm, frame_number : int) -> None:
progress.update(frame_number - progress.n)
def run_ffmpeg_with_pipe(commands : List[Command], file_content : Buffer) -> subprocess.Popen[Buffer]:
commands = ffmpeg_builder.run(commands)
process = subprocess.Popen(commands, stdin = subprocess.PIPE, stderr = subprocess.PIPE, stdout = subprocess.PIPE)
@@ -184,8 +178,9 @@ def extract_frames(target_path : str, output_path : str, temp_video_resolution :
ffmpeg_builder.set_output(temp_frames_pattern)
)
with tqdm(total = extract_frame_total, desc = translator.get('extracting'), unit = 'frame', ascii = ' =', disable = state_manager.get_item('log_level') in [ 'warn', 'error' ]) as progress:
process = run_ffmpeg_with_progress(commands, partial(update_progress, progress))
with cli_progress.create(total = extract_frame_total) as progress:
progress.set_title(translator.get('extracting'))
process = run_ffmpeg_with_progress(commands, progress.seek)
return process.returncode == 0
@@ -202,8 +197,9 @@ def spawn_frames(target_path : str, output_path : str, temp_video_resolution : R
ffmpeg_builder.set_output(temp_frames_pattern)
)
with tqdm(total = spawn_frame_total, desc = translator.get('spawning'), unit = 'frame', ascii = ' =', disable = state_manager.get_item('log_level') in [ 'warn', 'error' ]) as progress:
process = run_ffmpeg_with_progress(commands, partial(update_progress, progress))
with cli_progress.create(total = spawn_frame_total) as progress:
progress.set_title(translator.get('spawning'))
process = run_ffmpeg_with_progress(commands, progress.seek)
return process.returncode == 0
@@ -328,8 +324,9 @@ def merge_video(target_path : str, output_path : str, temp_video_fps : Fps, outp
ffmpeg_builder.force_output(temp_video_path)
)
with tqdm(total = merge_frame_total, desc = translator.get('merging'), unit = 'frame', ascii = ' =', disable = state_manager.get_item('log_level') in [ 'warn', 'error' ]) as progress:
process = run_ffmpeg_with_progress(commands, partial(update_progress, progress))
with cli_progress.create(total = merge_frame_total) as progress:
progress.set_title(translator.get('merging'))
process = run_ffmpeg_with_progress(commands, progress.seek)
return process.returncode == 0
+4 -3
View File
@@ -5,9 +5,8 @@ from concurrent.futures import ThreadPoolExecutor
from typing import Deque, Iterator, List
import cv2
from tqdm import tqdm
from facefusion import ffmpeg_builder, logger, state_manager, translator
from facefusion import cli_progress, ffmpeg_builder, logger, state_manager, translator
from facefusion.audio import create_empty_audio_frame
from facefusion.content_analyser import analyse_stream
from facefusion.ffmpeg import open_ffmpeg
@@ -21,7 +20,9 @@ def multi_process_capture(camera_capture : cv2.VideoCapture, camera_fps : Fps) -
capture_deque : Deque[VisionFrame] = deque()
source_vision_frames = read_static_images(state_manager.get_item('source_paths'))
with tqdm(desc = translator.get('streaming'), unit = 'frame', disable = state_manager.get_item('log_level') in [ 'warn', 'error' ]) as progress:
with cli_progress.create() as progress:
progress.set_title(translator.get('streaming'))
with ThreadPoolExecutor(max_workers = state_manager.get_item('execution_thread_count')) as executor:
futures = []
+4
View File
@@ -177,6 +177,10 @@ ErrorCode = Literal[0, 1, 2, 3, 4]
LogLevel = Literal['error', 'warn', 'info', 'debug']
LogLevelSet : TypeAlias = Dict[LogLevel, int]
ProgressAction = Literal['color_active', 'color_neutral', 'cursor_start', 'erase_line', 'reset']
ProgressActionSet : TypeAlias = Dict[ProgressAction, str]
ProgressUnit = Literal['percent', 'frame', 'download']
TableHeader : TypeAlias = str
TableContent : TypeAlias = Any
+4 -4
View File
@@ -3,9 +3,8 @@ from concurrent.futures import Future, ThreadPoolExecutor
from typing import Deque, List
import numpy
from tqdm import tqdm
from facefusion import logger, process_manager, state_manager, translator
from facefusion import cli_progress, logger, process_manager, state_manager, translator
from facefusion.audio import create_empty_audio_frame, get_audio_frame, get_voice_frame
from facefusion.common_helper import get_first
from facefusion.filesystem import filter_audio_paths, get_file_extension, has_audio, has_image, has_video
@@ -150,8 +149,9 @@ def process_frames() -> ErrorCode:
temp_frame_set = resolve_temp_frame_set(state_manager.get_temp_path(), state_manager.get_item('output_path'), state_manager.get_item('temp_frame_format'))
if temp_frame_set:
with tqdm(total = len(temp_frame_set), desc = translator.get('processing'), unit = 'frame', ascii = ' =', disable = state_manager.get_item('log_level') in [ 'warn', 'error' ]) as progress:
progress.set_postfix(execution_providers = state_manager.get_item('execution_providers'))
with cli_progress.create(unit = 'frame') as progress:
progress.set_title(translator.get('processing'))
progress.count(temp_frame_set)
with ThreadPoolExecutor(max_workers = state_manager.get_item('execution_thread_count')) as executor:
futures : Deque[Future[bool]] = deque()
+4 -4
View File
@@ -4,9 +4,8 @@ from typing import Deque
import cv2
import numpy
from tqdm import tqdm
from facefusion import content_analyser, ffmpeg, logger, process_manager, state_manager, translator, video_manager
from facefusion import cli_progress, content_analyser, ffmpeg, logger, process_manager, state_manager, translator, video_manager
from facefusion.common_helper import get_first, get_middle
from facefusion.filesystem import filter_audio_paths, is_video
from facefusion.media_helper import restrict_trim_frame
@@ -75,8 +74,9 @@ def process_memory_frames() -> ErrorCode:
if temp_frame_range:
video_writer = video_manager.get_writer(state_manager.get_item('output_path'), temp_video_fps, output_video_resolution, output_video_resolution, state_manager.get_item('output_video_fps'))
with tqdm(total = len(temp_frame_range), desc = translator.get('processing'), unit = 'frame', ascii = ' =', disable = state_manager.get_item('log_level') in [ 'warn', 'error' ]) as progress:
progress.set_postfix(execution_providers = state_manager.get_item('execution_providers'))
with cli_progress.create(unit = 'frame') as progress:
progress.set_title(translator.get('processing'))
progress.count(temp_frame_range)
read_static_video_frame(state_manager.get_item('target_path'), state_manager.get_item('reference_frame_number'))
-1
View File
@@ -4,7 +4,6 @@ onnxruntime==1.28.0
opencv-python-headless==5.0.0.93
psutil==7.2.2
python-multipart==0.0.28
tqdm==4.70.0
scipy==1.18.0
starlette==0.52.1
uvicorn==0.41.0