diff --git a/utils/.config.template.toml b/utils/.config.template.toml index cbdee5b..222fbf4 100644 --- a/utils/.config.template.toml +++ b/utils/.config.template.toml @@ -41,8 +41,8 @@ resolution_h = { optional = false, default = 1920, example = 2560, explantation zoom = { optional = true, default = 1, example = 1.1, explanation = "Sets the browser zoom level. Useful if you want the text larger.", type = "float", nmin = 0.1, nmax = 2, oob_error = "The text is really difficult to read at a zoom level higher than 2" } [settings.background] -background_video = { optional = true, default = "minecraft", example = "rocket-league", options = ["custom", "minecraft", "gta", "rocket-league", "motor-gta", "csgo-surf", "cluster-truck", "minecraft-2","multiversus","fall-guys","steep", ""], explanation = "Sets the background for the video based on game name" } -background_audio = { optional = true, default = "lofi", example = "chill-summer", options = ["custom", "lofi","lofi-2","chill-summer",""], explanation = "Sets the background audio for the video" } +background_video = { optional = true, type="str", default = "minecraft", example = "rocket-league", explanation = "Sets the background for the video based on game name" } +background_audio = { optional = true, type="str", default = "lofi", example = "chill-summer", explanation = "Sets the background audio for the video" } background_audio_volume = { optional = true, type = "float", nmin = 0, nmax = 1, default = 0.15, example = 0.05, explanation="Sets the volume of the background audio. If you don't want background audio, set it to 0.", oob_error = "The volume HAS to be between 0 and 1", input_error = "The volume HAS to be a float number between 0 and 1"} enable_extra_audio = { optional = true, type = "bool", default = false, example = false, explanation="Used if you want to render another video without background audio in a separate folder", input_error = "The value HAS to be true or false"} background_thumbnail = { optional = true, type = "bool", default = false, example = false, options = [true, false,], explanation = "Generate a thumbnail for the video (put a thumbnail.png file in the assets/backgrounds directory.)" } diff --git a/utils/background_audios.json b/utils/background_audios.json index 7f9e70e..5749565 100644 --- a/utils/background_audios.json +++ b/utils/background_audios.json @@ -24,5 +24,10 @@ "https://www.youtube.com/watch?v=5oF5lS0sajs", "custom.mp4", "BreakingCopyright" + ], + "MONST3R": [ + "https://www.youtube.com/watch?v=Pi0GapxyT0Q", + "MONST3R.mp4", + "Horror Music World" ] } diff --git a/utils/ffmpeg.py b/utils/ffmpeg.py index eee5e14..60d1b80 100644 --- a/utils/ffmpeg.py +++ b/utils/ffmpeg.py @@ -1,63 +1,69 @@ import ffmpeg from pydub import AudioSegment -from moviepy.editor import VideoFileClip, AudioFileClip +# from moviepy.editor import VideoFileClip, AudioFileClip # from tqdm import tqdm -from rich.progress import Progress +from rich.progress import Progress, BarColumn, TextColumn, TaskProgressColumn, TimeRemainingColumn 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 reversed(stdout_lines): - if "out_time_ms" in line: - out_time_ms_str = line.split("=")[1].strip() - if out_time_ms_str.isnumeric(): - duration=float(out_time_ms_str) / 1000000.0 - # print(f"Returning duration {duration} from ffmpeg null muxer out_time_ms for {filename}...") - return duration - stderr=ffmpeg_stderr.decode('UTF-8') - stderr_lines=stderr.splitlines() - stream_durations=[] - for line in reversed(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: - duration=max(stream_durations) # sum? - # print(f"Returning duration {duration} from ffmpeg null muxer stream duration for {filename}...") - return duration - if filename.lower().endswith('.mp3'): - try: - duration=float(AudioSegment.from_mp3(filename).duration_seconds) - # print(f"Returning duration {duration} from AudioSegment for {filename}...") - return duration - except: - pass - try: - duration=float(AudioFileClip(filename).duration) - # print(f"Returning duration {duration} from AudioFileClip for {filename}...") - return duration - except: - pass - if filename.lower().endswith('.mp4'): - try: - duration=float(VideoFileClip(filename).duration) - # print(f"Returning duration {duration} from VideoFileClip for {filename}...") - return duration - except: - pass + # 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 reversed(stdout_lines): + # if "out_time_ms" in line: + # out_time_ms_str = line.split("=")[1].strip() + # if out_time_ms_str.isnumeric(): + # duration=float(out_time_ms_str) / 1000000.0 + # # print(f"Returning duration {duration} from ffmpeg null muxer out_time_ms for {filename}...") + # return duration + # stderr=ffmpeg_stderr.decode('UTF-8') + # stderr_lines=stderr.splitlines() + # stream_durations=[] + # for line in reversed(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: + # duration=max(stream_durations) # sum? + # # print(f"Returning duration {duration} from ffmpeg null muxer stream duration for {filename}...") + # return duration + # if filename.lower().endswith('.mp3'): + # try: + # duration=float(AudioSegment.from_mp3(filename).duration_seconds) + # # print(f"Returning duration {duration} from AudioSegment for {filename}...") + # return duration + # except: + # pass + # try: + # duration=float(AudioFileClip(filename).duration) + # # print(f"Returning duration {duration} from AudioFileClip for {filename}...") + # return duration + # except: + # pass + # if filename.lower().endswith('.mp4'): + # try: + # duration=float(VideoFileClip(filename).duration) + # # print(f"Returning duration {duration} from VideoFileClip for {filename}...") + # return duration + # except: + # pass probe_info=ffmpeg.probe(filename) duration=float(probe_info["format"]["duration"]) # print(f"Returning duration {duration} from ffprobe for {filename}...") return duration def ffmpeg_progress_run(ffmpeg_cmd, length, progress_text='Rendering...'): - with Progress() as progress_bar: + progress_bar_columns = [ + TextColumn("[progress.description]{task.description}"), + BarColumn(), + TaskProgressColumn(show_speed=True), + TimeRemainingColumn(elapsed_when_finished=True), + ] + with Progress(*progress_bar_columns) 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(progress_text, total=100) def progress_tracker(progress) -> None: diff --git a/utils/openai.py b/utils/openai.py index 1d6db3f..fe5fed3 100644 --- a/utils/openai.py +++ b/utils/openai.py @@ -3,6 +3,7 @@ from utils import settings from utils.console import print_step, print_substep from utils.posttextparser import posttextparser from utils.timeout import timeout +from rich.progress import track # import math import sys import tiktoken @@ -37,7 +38,7 @@ def ai_rewrite_story(story_text): MAX_PART_REWORD_TOKENS=int(settings.config['ai']['openai_rewrite_chunk_max_tokens']) longer_or_shorter = "longer" if float(settings.config['ai']['openai_rewrite_length']) > 1.0 else "shorter" print_step( - f"Using OpenAI {settings.config['ai']['openai_model']} to rewrite the content..." + f"Using OpenAI {settings.config['ai']['openai_model']} to rewrite split portions of the story..." ) client = OpenAI( base_url=settings.config["ai"]["openai_api_base"], @@ -50,7 +51,7 @@ def ai_rewrite_story(story_text): ai_assistant_message = "Sure! What is the story?" base_tokens = num_tokens_from_string(ai_system_message+ai_user_message+ai_assistant_message, model_name) parts=posttextparser(story_text) - rewritten_parts=[] + parts_split_by_tokens=[] while len(parts) > 0: tmp_part_list=[] tmp_tokens = base_tokens @@ -59,51 +60,58 @@ def ai_rewrite_story(story_text): tmp_tokens+=num_tokens_from_string(next_part, model_name) tmp_part_list.append(next_part) if len(tmp_part_list) > 0: - joined_part_list=" ".join(tmp_part_list) - part_chat_history = [ - {"role":"system", "content":ai_system_message}, - {"role":"user", "content":ai_user_message}, - {"role":"assistant", "content":ai_assistant_message}, - {"role":"user", "content":joined_part_list} - ] - joined_part_list_tokens=num_tokens_from_string(joined_part_list, model_name) * float(settings.config['ai']['openai_rewrite_length']) - ai_part_message='' - part_retry_num=0 - while part_retry_num <= MAX_RETRIES and num_tokens_from_string(ai_part_message, model_name) < joined_part_list_tokens: - part_retry_text = '' if part_retry_num <= 0 else f"[Retry #{part_retry_num}]" - try: - with timeout(seconds=60): - part_log_message = f"{part_retry_text} Making request to OpenAI to make the portion of the story longer...".strip() if ai_part_message != '' else f"{part_retry_text} Making request to OpenAI to reword a portion of the story...".strip() - print_substep(part_log_message) - # print(part_chat_history) - ai_part_response = client.chat.completions.create( - model=model_name, - messages=part_chat_history, - temperature=0.9, # very creative - timeout=60 - # max_tokens=math.ceil(num_tokens_from_string(ai_selftext, model_name)*2.5) # 2.5 because it counts all the messages in history - ) - ai_part_message_updated=remove_ai_extras(ai_part_response.choices[0].message.content) - old_part_tokens = num_tokens_from_string(ai_part_message, model_name) - new_part_tokens = num_tokens_from_string(ai_part_message_updated, model_name) - if new_part_tokens > old_part_tokens and is_valid_ai_response(ai_part_message_updated): - ai_part_message = ai_part_message_updated - print_substep(f"Got AI response: {ai_part_message}") - part_chat_history.append({"role":"assistant", "content":ai_part_message}) - part_chat_history.append({"role":"user", "content":"Make the story longer/more detailed"}) - except KeyboardInterrupt: - sys.exit(1) - except Exception as e: - print_substep(str(e), style="bold red") - pass - part_retry_num+=1 - if not bool(ai_part_message): - if bool(settings.config['ai']['openai_retry_fail_error']): - raise ValueError('AI rewrite failed') - else: - ai_part_message = joined_part_list - rewritten_parts.append(ai_part_message) + parts_split_by_tokens.append(tmp_part_list[:]) tmp_part_list.clear() + rewritten_parts=[] + for part_list in track(parts_split_by_tokens, "✏️ Rewriting..."): + joined_part_list=" ".join(part_list) + part_chat_history = [ + {"role":"system", "content":ai_system_message}, + {"role":"user", "content":ai_user_message}, + {"role":"assistant", "content":ai_assistant_message}, + {"role":"user", "content":joined_part_list} + ] + joined_part_list_tokens=num_tokens_from_string(joined_part_list, model_name) * float(settings.config['ai']['openai_rewrite_length']) + ai_part_message='' + part_retry_num=0 + while part_retry_num <= MAX_RETRIES and num_tokens_from_string(ai_part_message, model_name) < joined_part_list_tokens: + # part_retry_text = '' if part_retry_num <= 0 else f"[Retry #{part_retry_num}]" + try: + with timeout(seconds=60): + # part_log_message = f"{part_retry_text} Making request to OpenAI to make the portion of the story longer...".strip() if ai_part_message != '' else f"{part_retry_text} Making request to OpenAI to reword a portion of the story...".strip() + # print_substep(part_log_message) + # print(part_chat_history) + ai_part_response = client.chat.completions.create( + model=model_name, + messages=part_chat_history, + temperature=0.9, # very creative + timeout=60 + # max_tokens=math.ceil(num_tokens_from_string(ai_selftext, model_name)*2.5) # 2.5 because it counts all the messages in history + ) + ai_part_message_updated=remove_ai_extras(ai_part_response.choices[0].message.content) + old_part_tokens = num_tokens_from_string(ai_part_message, model_name) + new_part_tokens = num_tokens_from_string(ai_part_message_updated, model_name) + if new_part_tokens > old_part_tokens and is_valid_ai_response(ai_part_message_updated): + ai_part_message = ai_part_message_updated + # print_substep(f"Got AI response: {ai_part_message}") + part_chat_history.append({"role":"assistant", "content":ai_part_message}) + part_chat_history.append({"role":"user", "content":"Make the story longer/more detailed"}) + except KeyboardInterrupt: + sys.exit(1) + except Exception as e: + print_substep(str(e), style="bold red") + pass + part_retry_num+=1 + if not bool(ai_part_message): + if bool(settings.config['ai']['openai_retry_fail_error']): + raise ValueError('AI rewrite failed') + else: + ai_part_message = joined_part_list + rewritten_parts.append(ai_part_message) + print_substep("Story sections rewritten successfully!", style="bold green") + print_step( + f"Using OpenAI {settings.config['ai']['openai_model']} to finalize the story..." + ) try: joined_rewritten_parts=" ".join(rewritten_parts) chat_history=[ @@ -116,11 +124,11 @@ def ai_rewrite_story(story_text): ai_message='' retry_num=0 while retry_num <= MAX_RETRIES and num_tokens_from_string(ai_message, model_name) < joined_rewritten_parts_tokens: - retry_text = '' if retry_num <= 0 else f"[Retry #{retry_num}]" + # retry_text = '' if retry_num <= 0 else f"[Retry #{retry_num}]" try: with timeout(seconds=90): - log_message = f"{retry_text} Making request to OpenAI to make the whole story longer...".strip() if ai_message != '' else f"{retry_text} Making request to OpenAI to finalize the whole story...".strip() - print_substep(log_message) + # log_message = f"{retry_text} Making request to OpenAI to make the whole story longer...".strip() if ai_message != '' else f"{retry_text} Making request to OpenAI to finalize the whole story...".strip() + # print_substep(log_message) # print(chat_history) ai_response = client.chat.completions.create( model=model_name, @@ -134,7 +142,7 @@ def ai_rewrite_story(story_text): new_tokens = num_tokens_from_string(ai_message_updated, model_name) if new_tokens > old_tokens and is_valid_ai_response(ai_message_updated): ai_message = ai_message_updated - print_substep(f"Got AI response: {ai_message}") + # print_substep(f"Got AI response: {ai_message}") chat_history.append({"role":"assistant", "content":ai_message}) chat_history.append({"role":"user", "content":"Make the story longer/more detailed"}) except KeyboardInterrupt: