From cb8c07f0c65397c874051e847aa04ad192492aed Mon Sep 17 00:00:00 2001 From: KyleBoyer Date: Mon, 20 Nov 2023 22:59:51 -0600 Subject: [PATCH] Standardize progress bars --- TTS/TikTok.py | 2 + TTS/engine_wrapper.py | 14 ++++++- utils/ffmpeg.py | 75 ++++++++++++++++++++++++++++-------- utils/ffmpeg_progress.py | 3 +- video_creation/background.py | 74 ++++++++++++++++++++--------------- 5 files changed, 118 insertions(+), 50 deletions(-) diff --git a/TTS/TikTok.py b/TTS/TikTok.py index 9049bb7..514cdb9 100644 --- a/TTS/TikTok.py +++ b/TTS/TikTok.py @@ -114,6 +114,8 @@ class TikTok: "The TikTok TTS returned an invalid response. Please try again later, and report this bug." ) raise TikTokTTSException(0, "Invalid response") + if not raw_voices: + raise TikTokTTSException(0, "Invalid response") decoded_voices = base64.b64decode(raw_voices) # write voices to specified filepath diff --git a/TTS/engine_wrapper.py b/TTS/engine_wrapper.py index 60730ec..33e9cc8 100644 --- a/TTS/engine_wrapper.py +++ b/TTS/engine_wrapper.py @@ -8,11 +8,12 @@ import numpy as np import translators from moviepy.audio.AudioClip import AudioClip from moviepy.audio.fx.volumex import volumex -from moviepy.editor import AudioFileClip +# from moviepy.editor import AudioFileClip from rich.progress import track from utils import settings from utils.console import print_step, print_substep +from utils.ffmpeg import get_duration # , ffmpeg_progress_run from utils.voice import sanitize_text from pydub import AudioSegment @@ -135,7 +136,14 @@ class TTSEngine: split_files.append(str(f"{self.path}/{idx}-{idy}.part.mp3")) # f.write("file " + f"'silence.mp3'" + "\n") concat_parts.append('silence.mp3') + # concat_parts_length = sum([ + # get_duration(post_audio_file) + # for post_audio_file in track(concat_parts, "Calculating the audio file durations...") + # ]) + # ffmpeg_progress_run( ffmpeg.concat(*concat_parts).output(f"{self.path}/{idx}.mp3").overwrite_output().global_args('-y -hide_banner -loglevel panic -safe 0').run(quiet=True) + # concat_parts_length + # ) # os.system( # "ffmpeg -f concat -y -hide_banner -loglevel panic -safe 0 " # + "-i " @@ -153,6 +161,7 @@ class TTSEngine: def call_tts(self, filename: str, text: str): mp3_filepath = f"{self.path}/{filename}.mp3" + # mp3_duration = get_duration(mp3_filepath) audio_speed = settings.config["settings"]["tts"]["speed"] mp3_speed_changed_filepath = f"{self.path}/{filename}-speed-{audio_speed}.mp3" self.tts_module.run( @@ -161,7 +170,10 @@ class TTSEngine: random_voice=settings.config["settings"]["tts"]["random_voice"], ) if audio_speed != 1: + # ffmpeg_progress_run( ffmpeg.input(mp3_filepath).filter("atempo", audio_speed).output(mp3_speed_changed_filepath).overwrite_output().run(quiet=True) + # mp3_duration*(1/audio_speed) + # ) os.replace(mp3_speed_changed_filepath, mp3_filepath) # try: diff --git a/utils/ffmpeg.py b/utils/ffmpeg.py index f5c1948..e07a31d 100644 --- a/utils/ffmpeg.py +++ b/utils/ffmpeg.py @@ -1,28 +1,69 @@ import ffmpeg from pydub import AudioSegment -from tqdm import tqdm +from moviepy.editor import VideoFileClip, AudioFileClip +# from tqdm import tqdm +from rich.progress import Progress from utils.ffmpeg_progress import ProgressFfmpeg def get_duration(filename): + ffmpeg_cmd = ffmpeg.input(filename).output('/dev/null', f="null", progress='/dev/stdout') + ffmpeg_stdout, ffmpeg_stderr = ffmpeg_cmd.run(capture_stdout=True, capture_stderr=True) + stdout=ffmpeg_stdout.decode('UTF-8') + stdout_lines=stdout.splitlines() + for line in stdout_lines: + if "out_time_ms" in line: + out_time_ms_str = line.split("=")[1].strip() + if out_time_ms_str.isnumeric(): + return float(out_time_ms_str) / 1000000.0 + stderr=ffmpeg_stderr.decode('UTF-8') + stderr_lines=stderr.splitlines() + stream_durations=[] + for line in stderr_lines: + if "Duration:" in line: + timestamp = line.split("Duration:")[1].strip().split(',')[0].strip() + h, m, s_ms = timestamp.split(':') + s, ms = s_ms.split('.') + stream_durations.append(int(h) * 3600 + int(m) * 60 + int(s) + float(f".{ms}")) + if len(stream_durations) > 0: + return max(stream_durations) if filename.lower().endswith('.mp3'): - return float(AudioSegment.from_mp3(filename).duration_seconds) + try: + return float(AudioSegment.from_mp3(filename).duration_seconds) + except: + pass + try: + return float(AudioFileClip(filename).duration) + except: + pass + if filename.lower().endswith('.mp4'): + try: + return float(VideoFileClip(filename).duration) + except: + pass probe_info=ffmpeg.probe(filename) return float(probe_info["format"]["duration"]) def ffmpeg_progress_run(ffmpeg_cmd, length): - pbar = tqdm(total=100, desc="Progress: ", bar_format="{l_bar}{bar}", unit=" %", dynamic_ncols=True, leave=False) - def progress_tracker(progress) -> None: - status = round(progress * 100, 2) - old_percentage = pbar.n - pbar.update(status - old_percentage) - with ProgressFfmpeg(length, progress_tracker) as progress: - ffmpeg_cmd.global_args("-progress", progress.output_file.name).run( - quiet=True, - overwrite_output=True, - capture_stdout=False, - capture_stderr=False, - ) - old_percentage = pbar.n - pbar.update(100 - old_percentage) - pbar.close() \ No newline at end of file + with Progress() as progress_bar: + # pbar = tqdm(total=100, desc="Progress: ", bar_format="{l_bar}{bar}", unit=" %", dynamic_ncols=True, leave=False) + task = progress_bar.add_task("Rendering...", total=100) + def progress_tracker(progress) -> None: + new_progress=progress*100 + if new_progress >= progress_bar._tasks[task].completed: + progress_bar.update(task, completed=new_progress) + # status = round(progress * 100, 2) + # old_percentage = pbar.n + # pbar.update(status - old_percentage) + with ProgressFfmpeg(length, progress_tracker) as progress: + progress_bar.start_task(task) + ffmpeg_cmd.global_args("-progress", progress.output_file.name).run( + quiet=True, + overwrite_output=True, + capture_stdout=False, + capture_stderr=False, + ) + # old_percentage = pbar.n + # pbar.update(100 - old_percentage) + # pbar.close() + progress_bar.update(task, completed=100) \ No newline at end of file diff --git a/utils/ffmpeg_progress.py b/utils/ffmpeg_progress.py index 56af3af..2805f29 100644 --- a/utils/ffmpeg_progress.py +++ b/utils/ffmpeg_progress.py @@ -15,11 +15,10 @@ class ProgressFfmpeg(threading.Thread): latest_progress = self.get_latest_ms_progress() completed_percent = latest_progress / self.vid_duration_seconds self.progress_update_callback(completed_percent) - time.sleep(1) + time.sleep(0.1) def get_latest_ms_progress(self): lines = self.output_file.readlines() - if lines: for line in lines: if "out_time_ms" in line: diff --git a/video_creation/background.py b/video_creation/background.py index 1761d0c..22f95f5 100644 --- a/video_creation/background.py +++ b/video_creation/background.py @@ -6,14 +6,14 @@ from pathlib import Path from random import randrange from typing import Any, Tuple, Dict -from moviepy.editor import VideoFileClip, AudioFileClip -from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip +# from moviepy.editor import VideoFileClip, AudioFileClip +# from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip from utils import settings from utils.console import print_step, print_substep import yt_dlp import ffmpeg -from utils.ffmpeg import ffmpeg_progress_run +from utils.ffmpeg import ffmpeg_progress_run, get_duration def load_background_options(): @@ -137,10 +137,10 @@ def chop_background(background_config: Dict[str, Tuple], video_length: int, redd else: audio_choice = f"{background_config['audio'][2]}-{background_config['audio'][1]}" audio_file_path=f"assets/backgrounds/audio/{audio_choice}" + audio_file_duration=get_duration(audio_file_path) if bool(settings.config["settings"]["background"][f"background_audio_loop"]): background_looped_audio_file_path = f"assets/backgrounds/audio/looped-{audio_choice}" - background_audio_duration = float(ffmpeg.probe(audio_file_path)["format"]["duration"]) - background_audio_loops = math.ceil(video_length / background_audio_duration) + background_audio_loops = math.ceil(video_length / audio_file_duration) if background_audio_loops > 1: print_step(f"Looping background audio {background_audio_loops} times...🔁") background_audio_loop_input = ffmpeg.input( @@ -154,24 +154,35 @@ def chop_background(background_config: Dict[str, Tuple], video_length: int, redd vcodec="copy", acodec="copy" ).overwrite_output(), - background_audio_loops*background_audio_duration + background_audio_loops*audio_file_duration ) audio_file_path = background_looped_audio_file_path + audio_file_duration = audio_file_duration*background_audio_loops print_step("Finding a spot in the background audio to chop...✂️") - background_audio = AudioFileClip(audio_file_path) start_time_audio, end_time_audio = get_start_and_end_times( - video_length, background_audio.duration + video_length, audio_file_duration + ) + # background_audio = background_audio.subclip(start_time_audio, end_time_audio) + # background_audio.write_audiofile(f"assets/temp/{id}/background.mp3") + # background_audio.close() + ffmpeg_progress_run( + ffmpeg.input(audio_file_path).output( + f"assets/temp/{id}/background.mp3", + # acodec="copy", + # acodec="libmp3lame", + map="0", + ss=start_time_audio, + to=end_time_audio + ).overwrite_output(), + end_time_audio-start_time_audio ) - background_audio = background_audio.subclip(start_time_audio, end_time_audio) - background_audio.write_audiofile(f"assets/temp/{id}/background.mp3") - background_audio.close() video_choice = f"{background_config['video'][2]}-{background_config['video'][1]}" video_file_path = f"assets/backgrounds/video/{video_choice}" + video_file_duration=get_duration(video_file_path) if bool(settings.config["settings"]["background"][f"background_video_loop"]): background_looped_video_file_path = f"assets/backgrounds/video/looped-{video_choice}" - background_video_duration = float(ffmpeg.probe(video_file_path)["format"]["duration"]) - background_video_loops = math.ceil(video_length / background_video_duration) + background_video_loops = math.ceil(video_length / video_file_duration) if background_video_loops > 1: print_step(f"Looping background video {background_video_loops} times...🔁") background_video_loop_input = ffmpeg.input( @@ -185,30 +196,33 @@ def chop_background(background_config: Dict[str, Tuple], video_length: int, redd vcodec="copy", acodec="copy" ).overwrite_output(), - background_video_loops*background_video_duration + background_video_loops*video_file_duration ) video_file_path = background_looped_video_file_path + video_file_duration = video_file_duration * background_video_loops print_step("Finding a spot in the background video to chop...✂️") - background_video = VideoFileClip(video_file_path) start_time_video, end_time_video = get_start_and_end_times( - video_length, background_video.duration + video_length, video_file_duration ) - background_video.close() # Extract video subclip - try: - ffmpeg_extract_subclip( - video_file_path, - start_time_video, - end_time_video, - targetname=f"assets/temp/{id}/background.mp4", - ) - except (OSError, IOError): # ffmpeg issue see #348 - print_substep("FFMPEG issue. Trying again...") - video=VideoFileClip(video_file_path) - new = video.subclip(start_time_video, end_time_video) - new.write_videofile(f"assets/temp/{id}/background.mp4") - video.close() + # try: + ffmpeg_progress_run( + ffmpeg.input(video_file_path).output(f"assets/temp/{id}/background.mp4", vcodec="copy", acodec="copy", map="0", ss=start_time_video, to=end_time_video), + end_time_video-start_time_video + ) + # ffmpeg_extract_subclip( + # video_file_path, + # start_time_video, # -ss + # end_time_video, # -to + # targetname=f"assets/temp/{id}/background.mp4", + # ) + # except (OSError, IOError): # ffmpeg issue see #348 + # print_substep("FFMPEG issue. Trying again...") + # video=VideoFileClip(video_file_path) + # new = video.subclip(start_time_video, end_time_video) + # new.write_videofile(f"assets/temp/{id}/background.mp4") + # video.close() print_substep("Background video chopped successfully!", style="bold green") return background_config["video"][2]