From db18abd4265df43fc6317caae2df16b42bb66362 Mon Sep 17 00:00:00 2001 From: Immelancholy Date: Tue, 13 May 2025 09:49:27 +0100 Subject: [PATCH 01/15] Drastically reduced time to render cache by running the chafa process for each frame on a new thread xd --- anifetch.py | 128 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 86 insertions(+), 42 deletions(-) diff --git a/anifetch.py b/anifetch.py index 444b81f..ee313bf 100644 --- a/anifetch.py +++ b/anifetch.py @@ -1,3 +1,5 @@ +import threading +import threading import argparse import json import os @@ -6,6 +8,7 @@ import subprocess import sys import time +import threading def print_verbose(*msg): @@ -188,35 +191,30 @@ def check_sound_flag(): WIDTH = args.width HEIGHT = args.height - # put cached frames here frames: list[str] = [] -# cache is invalid, re-render -if should_update: - print_verbose("SHOULD RENDER WITH CHAFA") - - # delete all old frames - shutil.rmtree(BASE_PATH / "video") - os.mkdir(BASE_PATH / "video") - +def ffmpeg_process(): stdout = None if args.verbose else subprocess.DEVNULL stderr = None if args.verbose else subprocess.STDOUT - - - subprocess.call( + ffmpeg = subprocess.Popen( [ "ffmpeg", "-i", f"{args.filename}", "-vf", f"fps={args.framerate},format=rgba", - str(BASE_PATH / "video/%05d.png"), + str(BASE_PATH / "video/%d.png"), ], stdout=stdout, stderr=stderr, ) + ffmpeg.wait() + +thread_ffmpeg = threading.Thread(target=ffmpeg_process) + +def get_sound(): print_verbose(args.sound_flag_given) if args.sound_flag_given: @@ -237,6 +235,69 @@ def check_sound_flag(): print_verbose(args.sound_saved_path) +thread_sound = threading.Thread(target=get_sound) + +def chafa_process(f): + WIDTH = args.width + HEIGHT = args.height + # for i, f in enumerate(animation_files): + # TODO: REMOVE THIS + #print_verbose(f"- Frame: {f}") + + # f = 00001.png + chafa_args = args.chafa_arguments.strip() + chafa_args += " --format symbols" # Fixes https://github.com/Notenlish/anifetch/issues/1 + + path = BASE_PATH / "video" / f + chafa_cmd = [ + "chafa", + *chafa_args.split(" "), + # "--color-space=rgb", + f"--size={WIDTH}x{HEIGHT}", + path.as_posix(), + ] + frame = subprocess.check_output( + chafa_cmd, + text=True, + ) + + with open((BASE_PATH / "output" / f).with_suffix(".txt"), "w") as file: + file.write(frame) + + # if wanted aspect ratio doesnt match source, chafa makes width as high as it can, and adjusts height accordingly. + # AKA: even if I specify 40x20, chafa might give me 40x11 or something like that. + animation_files = os.listdir(BASE_PATH / "video") + chafa_files = os.listdir(BASE_PATH / "output") + if len(animation_files) == len(chafa_files): + HEIGHT = len(frame.splitlines()) + frames.append(frame) # dont question this, I need frames to have at least a single item + +def files_new(): + animation_files = os.listdir(BASE_PATH / "video") + while len(animation_files) == 0: + animation_files = os.listdir(BASE_PATH / "video") + chafa_files = os.listdir(BASE_PATH / "output") + threads = [] + for i, f in enumerate(animation_files): + thread_chafa = threading.Thread(target=chafa_process, args=(f, )) + + thread_chafa.start() + threads.append(thread_chafa) + print_verbose("Launching chafa threads") + + for i in enumerate(threads): + thread_chafa.join() + +thread_chafa = threading.Thread(target=files_new) + + +# cache is invalid, re-render +if should_update: + print_verbose("SHOULD RENDER WITH CHAFA") + + # delete all old frames + shutil.rmtree(BASE_PATH / "video") + os.mkdir(BASE_PATH / "video") # If the new anim frames is shorter than the old one, then in /output there will be both new and old frames. Empty the directory to fix this. shutil.rmtree(BASE_PATH / "output") @@ -244,38 +305,21 @@ def check_sound_flag(): print_verbose("Emptied the output folder.") - # get the frames - animation_files = os.listdir(BASE_PATH / "video") - animation_files.sort() - for i, f in enumerate(animation_files): - # TODO: REMOVE THIS - #print_verbose(f"- Frame: {f}") + thread_sound.start() - # f = 00001.png - chafa_args = args.chafa_arguments.strip() - chafa_args += " --format symbols" # Fixes https://github.com/Notenlish/anifetch/issues/1 - - path = BASE_PATH / "video" / f - chafa_cmd = [ - "chafa", - *chafa_args.split(" "), - # "--color-space=rgb", - f"--size={WIDTH}x{HEIGHT}", - path.as_posix(), - ] - frame = subprocess.check_output( - chafa_cmd, - text=True, - ) + thread_ffmpeg.start() + + thread_ffmpeg.join() - with open((BASE_PATH / "output" / f).with_suffix(".txt"), "w") as file: - file.write(frame) + thread_sound.join() + + files_new() - # if wanted aspect ratio doesnt match source, chafa makes width as high as it can, and adjusts height accordingly. - # AKA: even if I specify 40x20, chafa might give me 40x11 or something like that. - if i == 0: - HEIGHT = len(frame.splitlines()) - frames.append(frame) # dont question this, I need frames to have at least a single item + + + + + # get the frames else: # just use cached for filename in os.listdir(BASE_PATH / "output"): From a010612fb8ad0ad3f4c9fd8b4274ae41fde00176 Mon Sep 17 00:00:00 2001 From: Immelancholy Date: Tue, 13 May 2025 09:58:01 +0100 Subject: [PATCH 02/15] Remove unnecessary lines of code --- anifetch.py | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/anifetch.py b/anifetch.py index ee313bf..aaad308 100644 --- a/anifetch.py +++ b/anifetch.py @@ -237,7 +237,7 @@ def get_sound(): thread_sound = threading.Thread(target=get_sound) -def chafa_process(f): +def chafa_process(f, i): WIDTH = args.width HEIGHT = args.height # for i, f in enumerate(animation_files): @@ -266,20 +266,15 @@ def chafa_process(f): # if wanted aspect ratio doesnt match source, chafa makes width as high as it can, and adjusts height accordingly. # AKA: even if I specify 40x20, chafa might give me 40x11 or something like that. - animation_files = os.listdir(BASE_PATH / "video") - chafa_files = os.listdir(BASE_PATH / "output") - if len(animation_files) == len(chafa_files): + if i == 0: HEIGHT = len(frame.splitlines()) frames.append(frame) # dont question this, I need frames to have at least a single item -def files_new(): - animation_files = os.listdir(BASE_PATH / "video") - while len(animation_files) == 0: - animation_files = os.listdir(BASE_PATH / "video") - chafa_files = os.listdir(BASE_PATH / "output") +def chafa_files(): threads = [] + animation_files = os.listdir(BASE_PATH / "video") for i, f in enumerate(animation_files): - thread_chafa = threading.Thread(target=chafa_process, args=(f, )) + thread_chafa = threading.Thread(target=chafa_process, args=(f, i, )) thread_chafa.start() threads.append(thread_chafa) @@ -288,9 +283,6 @@ def files_new(): for i in enumerate(threads): thread_chafa.join() -thread_chafa = threading.Thread(target=files_new) - - # cache is invalid, re-render if should_update: print_verbose("SHOULD RENDER WITH CHAFA") @@ -313,7 +305,7 @@ def files_new(): thread_sound.join() - files_new() + chafa_files() From e9bcf4b38068d0b59d95268dfbc7d1f27818395f Mon Sep 17 00:00:00 2001 From: Immelancholy Date: Tue, 13 May 2025 13:29:57 +0100 Subject: [PATCH 03/15] I think that's full multithreading added right there --- anifetch.py | 85 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 50 insertions(+), 35 deletions(-) diff --git a/anifetch.py b/anifetch.py index aaad308..452fe89 100644 --- a/anifetch.py +++ b/anifetch.py @@ -194,25 +194,6 @@ def check_sound_flag(): # put cached frames here frames: list[str] = [] -def ffmpeg_process(): - stdout = None if args.verbose else subprocess.DEVNULL - stderr = None if args.verbose else subprocess.STDOUT - ffmpeg = subprocess.Popen( - [ - "ffmpeg", - "-i", - f"{args.filename}", - "-vf", - f"fps={args.framerate},format=rgba", - str(BASE_PATH / "video/%d.png"), - ], - stdout=stdout, - stderr=stderr, - ) - ffmpeg.wait() - - -thread_ffmpeg = threading.Thread(target=ffmpeg_process) def get_sound(): print_verbose(args.sound_flag_given) @@ -266,22 +247,56 @@ def chafa_process(f, i): # if wanted aspect ratio doesnt match source, chafa makes width as high as it can, and adjusts height accordingly. # AKA: even if I specify 40x20, chafa might give me 40x11 or something like that. - if i == 0: - HEIGHT = len(frame.splitlines()) - frames.append(frame) # dont question this, I need frames to have at least a single item + HEIGHT = len(frame.splitlines()) + frames.append(frame) # dont question this, I need frames to have at least a single item -def chafa_files(): +code = [str] +def chafa_files(code): threads = [] animation_files = os.listdir(BASE_PATH / "video") - for i, f in enumerate(animation_files): - thread_chafa = threading.Thread(target=chafa_process, args=(f, i, )) - - thread_chafa.start() - threads.append(thread_chafa) - print_verbose("Launching chafa threads") + print_verbose("checking dir for changes") + while len(animation_files) == 0: + animation_files = os.listdir(BASE_PATH / "video") + # for i, f in enumerate(animation_files): + i = 1 + # sleep_time = args.framerate / 1000 + while len(code) == 1: + # time.sleep(sleep_time) + f = str(i) + ".png" + path = BASE_PATH / "video" / f + if os.path.exists(path): + last_f = f + thread_chafa = threading.Thread(target=chafa_process, args=(f, i, )) + thread_chafa.start() + threads.append(thread_chafa) + print_verbose("Launching chafa threads") + i = i + 1 + for i in enumerate(threads): thread_chafa.join() +thread_files = threading.Thread(target=chafa_files, args=(code, )) + +def ffmpeg_process(): + stdout = None if args.verbose else subprocess.DEVNULL + stderr = None if args.verbose else subprocess.STDOUT + ffmpeg = subprocess.Popen( + [ + "ffmpeg", + "-i", + f"{args.filename}", + "-vf", + f"fps={args.framerate},format=rgba", + str(BASE_PATH / "video/%d.png"), + ], + stdout=stdout, + stderr=stderr, + ) + ffmpeg.wait() + code.append("done") + + +thread_ffmpeg = threading.Thread(target=ffmpeg_process) # cache is invalid, re-render if should_update: @@ -297,18 +312,18 @@ def chafa_files(): print_verbose("Emptied the output folder.") - thread_sound.start() + thread_files.start() thread_ffmpeg.start() - - thread_ffmpeg.join() - thread_sound.join() - - chafa_files() + thread_sound.start() + thread_ffmpeg.join() + + thread_sound.join() + thread_files.join() # get the frames From cce31a140a130a8d1508d7e8f723a42bcab97639 Mon Sep 17 00:00:00 2001 From: Immelancholy Date: Tue, 13 May 2025 16:15:55 +0100 Subject: [PATCH 04/15] Added a 0.02s sleep call to hopefully stop race condition between chafa and ffmpeg --- anifetch.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/anifetch.py b/anifetch.py index 452fe89..8209e0f 100644 --- a/anifetch.py +++ b/anifetch.py @@ -218,14 +218,10 @@ def get_sound(): thread_sound = threading.Thread(target=get_sound) -def chafa_process(f, i): +def chafa_process(f): WIDTH = args.width HEIGHT = args.height - # for i, f in enumerate(animation_files): - # TODO: REMOVE THIS - #print_verbose(f"- Frame: {f}") - # f = 00001.png chafa_args = args.chafa_arguments.strip() chafa_args += " --format symbols" # Fixes https://github.com/Notenlish/anifetch/issues/1 @@ -257,16 +253,15 @@ def chafa_files(code): print_verbose("checking dir for changes") while len(animation_files) == 0: animation_files = os.listdir(BASE_PATH / "video") - # for i, f in enumerate(animation_files): i = 1 - # sleep_time = args.framerate / 1000 + sleep_time = 2 / 100 while len(code) == 1: - # time.sleep(sleep_time) + time.sleep(sleep_time) f = str(i) + ".png" path = BASE_PATH / "video" / f if os.path.exists(path): last_f = f - thread_chafa = threading.Thread(target=chafa_process, args=(f, i, )) + thread_chafa = threading.Thread(target=chafa_process, args=(f, )) thread_chafa.start() threads.append(thread_chafa) print_verbose("Launching chafa threads") From 886cd1c9ee0cad69342976024e9ce5d099e0cfef Mon Sep 17 00:00:00 2001 From: Immelancholy Date: Tue, 13 May 2025 23:43:42 +0100 Subject: [PATCH 05/15] remove extra "Import Threadings" lol --- anifetch.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/anifetch.py b/anifetch.py index 8209e0f..ad6c279 100644 --- a/anifetch.py +++ b/anifetch.py @@ -1,5 +1,3 @@ -import threading -import threading import argparse import json import os From 56ffabca1b9c9461b0d1181a089b0d70a0bb18ed Mon Sep 17 00:00:00 2001 From: Immelancholy Date: Wed, 14 May 2025 12:12:50 +0100 Subject: [PATCH 06/15] remove redundant variable --- anifetch.py | 1 - 1 file changed, 1 deletion(-) diff --git a/anifetch.py b/anifetch.py index ad6c279..d02ce36 100644 --- a/anifetch.py +++ b/anifetch.py @@ -258,7 +258,6 @@ def chafa_files(code): f = str(i) + ".png" path = BASE_PATH / "video" / f if os.path.exists(path): - last_f = f thread_chafa = threading.Thread(target=chafa_process, args=(f, )) thread_chafa.start() threads.append(thread_chafa) From f0cb1aa4a0dc81e2db703e5301717fc56e3ed8c1 Mon Sep 17 00:00:00 2001 From: Immelancholy Date: Wed, 14 May 2025 12:26:45 +0100 Subject: [PATCH 07/15] Add or statement to thread gen while loop so that it keeps going after ffmpeg is finished if there are more files in the directory to do --- anifetch.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/anifetch.py b/anifetch.py index d02ce36..20c88be 100644 --- a/anifetch.py +++ b/anifetch.py @@ -253,8 +253,11 @@ def chafa_files(code): animation_files = os.listdir(BASE_PATH / "video") i = 1 sleep_time = 2 / 100 - while len(code) == 1: + chafa_files = os.listdir(BASE_PATH / "output") + while len(code) == 1 or len(animation_files) != len(chafa_files): + animation_files = os.listdir(BASE_PATH / "video") time.sleep(sleep_time) + chafa_files = os.listdir(BASE_PATH / "output") f = str(i) + ".png" path = BASE_PATH / "video" / f if os.path.exists(path): From 47c04bfcb687adc12b354ce962d39f41aaa501e5 Mon Sep 17 00:00:00 2001 From: Immelancholy Date: Fri, 16 May 2025 22:02:03 +0100 Subject: [PATCH 08/15] Oops re-add this --- anifetch.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/anifetch.py b/anifetch.py index 1b8279c..070d373 100644 --- a/anifetch.py +++ b/anifetch.py @@ -133,7 +133,7 @@ def check_chroma_flag(): "--chroma", required=False, nargs="?", - help="Add this argument to chromakey a hexadecimal color from the video using ffmpeg e.g. '--chroma 0xc82044:0.1:0.1'", + help="Add this argument to chromakey a hexadecimal color from the video using ffmpeg using syntax of '--chroma ::' with being 0xRRGGBB with a 0x as opposed to a # e.g. '--chroma 0xc82044:0.1:0.1'", type=str, ) args = parser.parse_args() @@ -160,7 +160,9 @@ def check_chroma_flag(): codec = check_codec_of_file(args.filename) ext = get_ext_from_codec(codec) args.sound_saved_path = str(BASE_PATH / f"output_audio.{ext}") - +if args.chroma_flag_given: + if args.chroma.startswith("#"): + sys.exit("Color for hex code starts with an '0x'! Not a '#'") # check cache old_filename = "" From 369718fc69c2f539190831d21779795eeedbd741 Mon Sep 17 00:00:00 2001 From: Immelancholy Date: Tue, 20 May 2025 10:57:10 +0100 Subject: [PATCH 09/15] Remove sleep time in place of filesize check --- anifetch.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/anifetch.py b/anifetch.py index 070d373..b65e7b4 100644 --- a/anifetch.py +++ b/anifetch.py @@ -266,15 +266,19 @@ def chafa_files(code): while len(animation_files) == 0: animation_files = os.listdir(BASE_PATH / "video") i = 1 - sleep_time = 2 / 100 chafa_files = os.listdir(BASE_PATH / "output") while len(code) == 1 or len(animation_files) != len(chafa_files): animation_files = os.listdir(BASE_PATH / "video") - time.sleep(sleep_time) chafa_files = os.listdir(BASE_PATH / "output") f = str(i) + ".png" path = BASE_PATH / "video" / f + if os.path.exists(path): + statfile = os.stat(path) + filesize = statfile.st_size + while filesize == 0: + statfile = os.stat(path) + filesize = statfile.st_size thread_chafa = threading.Thread(target=chafa_process, args=(f, )) thread_chafa.start() threads.append(thread_chafa) From 413bc05b77868cfbbff82b63e9e9e29625f371cf Mon Sep 17 00:00:00 2001 From: Immelancholy Date: Tue, 20 May 2025 11:29:37 +0100 Subject: [PATCH 10/15] Better option but uses pillow --- anifetch.py | 14 +++++++++----- nix/packages/anifetch.nix | 1 + 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/anifetch.py b/anifetch.py index b65e7b4..c492b0b 100644 --- a/anifetch.py +++ b/anifetch.py @@ -7,7 +7,14 @@ import sys import time import threading +from PIL import Image +def is_valid_image(file_path): + try: + Image.open(file_path) + return True + except: + return False def print_verbose(*msg): if args.verbose: @@ -274,11 +281,8 @@ def chafa_files(code): path = BASE_PATH / "video" / f if os.path.exists(path): - statfile = os.stat(path) - filesize = statfile.st_size - while filesize == 0: - statfile = os.stat(path) - filesize = statfile.st_size + while is_valid_image(path) == False: + is_valid_image(path) thread_chafa = threading.Thread(target=chafa_process, args=(f, )) thread_chafa.start() threads.append(thread_chafa) diff --git a/nix/packages/anifetch.nix b/nix/packages/anifetch.nix index 2a085ca..0fca694 100644 --- a/nix/packages/anifetch.nix +++ b/nix/packages/anifetch.nix @@ -20,6 +20,7 @@ in pkgs.bc pkgs.chafa pkgs.ffmpeg + pkgs.python3Packages.pillow loop ]; preBuild = '' From 9af95c206d9c7853df2c2d23da9de841788403c5 Mon Sep 17 00:00:00 2001 From: Immelancholy Date: Tue, 20 May 2025 14:26:20 +0100 Subject: [PATCH 11/15] Add devshell and make valid img check more efficient --- anifetch.py | 4 +--- flake.nix | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/anifetch.py b/anifetch.py index c492b0b..65be1d2 100644 --- a/anifetch.py +++ b/anifetch.py @@ -280,9 +280,7 @@ def chafa_files(code): f = str(i) + ".png" path = BASE_PATH / "video" / f - if os.path.exists(path): - while is_valid_image(path) == False: - is_valid_image(path) + if is_valid_image(path): thread_chafa = threading.Thread(target=chafa_process, args=(f, )) thread_chafa.start() threads.append(thread_chafa) diff --git a/flake.nix b/flake.nix index 57353be..1d8853b 100644 --- a/flake.nix +++ b/flake.nix @@ -24,5 +24,23 @@ overlays = import ./nix/overlays; formatter = forAllSystems (system: nixpkgs.legacyPackages.${system}.alejandra); + devShells = forAllSystems (system: let + pkgs = import nixpkgs {inherit system;}; + in { + default = pkgs.mkShell { + packages = [ + (pkgs.python3.withPackages (python-pkgs: [ + python-pkgs.pillow + ])) + pkgs.neofetch + pkgs.fastfetch + pkgs.chafa + pkgs.bc + (pkgs.callPackage ./nix/packages/anifetch.nix {}) + ]; + shellHook = '' + ''; + }; + }); }; } From a1b7fa130de2059fbe6b5e05481c98da82521b16 Mon Sep 17 00:00:00 2001 From: Immelancholy Date: Tue, 20 May 2025 19:16:28 +0100 Subject: [PATCH 12/15] Fixed pillow check --- anifetch.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/anifetch.py b/anifetch.py index 65be1d2..149bafd 100644 --- a/anifetch.py +++ b/anifetch.py @@ -10,10 +10,14 @@ from PIL import Image def is_valid_image(file_path): - try: - Image.open(file_path) - return True - except: + if os.path.exists(file_path): + try: + im = Image.open(file_path) + im.verify() + return True + except Exception or IOError: + return False + else: return False def print_verbose(*msg): From 089bc2689c8049fb59eefba51a0ee9435813e9ac Mon Sep 17 00:00:00 2001 From: Immelancholy Date: Wed, 21 May 2025 15:30:26 +0100 Subject: [PATCH 13/15] Add numpy and create a much more efficient way of converting frames to chafa --- anifetch.py | 146 ++++++++++++++++++++------------------ flake.nix | 1 + nix/packages/anifetch.nix | 1 + 3 files changed, 77 insertions(+), 71 deletions(-) diff --git a/anifetch.py b/anifetch.py index 149bafd..3628186 100644 --- a/anifetch.py +++ b/anifetch.py @@ -8,17 +8,18 @@ import time import threading from PIL import Image - -def is_valid_image(file_path): - if os.path.exists(file_path): - try: - im = Image.open(file_path) - im.verify() - return True - except Exception or IOError: - return False - else: - return False +import numpy as np + +# def is_valid_image(file_path): +# if os.path.exists(file_path): +# try: +# im = Image.open(file_path) +# im.verify() +# return True +# except Exception or IOError: +# return False +# else: +# return False def print_verbose(*msg): if args.verbose: @@ -41,6 +42,16 @@ def check_codec_of_file(file:str): codec = str(subprocess.check_output(ffprobe_cmd, text=True).strip()) return codec +def get_width_of_video(file:str): + ffprobe_cmd = ["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=width", "-of", "default=nw=1:nk=1", file] + vid_width = int(subprocess.check_output(ffprobe_cmd, text=True)) + return vid_width + +def get_height_of_video(file:str): + ffprobe_cmd = ["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=height", "-of", "default=nw=1:nk=1", file] + vid_height = int(subprocess.check_output(ffprobe_cmd, text=True)) + return vid_height + def extract_audio_from_file(file:str, extension): audio_file = BASE_PATH / f"output_audio.{extension}" extract_cmd = ["ffmpeg", "-i", file, "-y", "-vn", "-c:a", "copy", "-loglevel","quiet", audio_file] @@ -241,7 +252,7 @@ def get_sound(): thread_sound = threading.Thread(target=get_sound) -def chafa_process(f): +def chafa_process(f, ffmpeg_frame): WIDTH = args.width HEIGHT = args.height @@ -249,6 +260,8 @@ def chafa_process(f): chafa_args += " --format symbols" # Fixes https://github.com/Notenlish/anifetch/issues/1 path = BASE_PATH / "video" / f + im = Image.fromarray(ffmpeg_frame) + im.save(path, compress_level=0) chafa_cmd = [ "chafa", *chafa_args.split(" "), @@ -269,65 +282,61 @@ def chafa_process(f): HEIGHT = len(frame.splitlines()) frames.append(frame) # dont question this, I need frames to have at least a single item -code = [str] -def chafa_files(code): +def ffmpeg_process(): threads = [] - animation_files = os.listdir(BASE_PATH / "video") - print_verbose("checking dir for changes") - while len(animation_files) == 0: - animation_files = os.listdir(BASE_PATH / "video") - i = 1 - chafa_files = os.listdir(BASE_PATH / "output") - while len(code) == 1 or len(animation_files) != len(chafa_files): - animation_files = os.listdir(BASE_PATH / "video") - chafa_files = os.listdir(BASE_PATH / "output") + stderr = None if args.verbose else subprocess.PIPE + vid_width = get_width_of_video(args.filename) + vid_height = get_height_of_video(args.filename) + if args.chroma_flag_given: + ffmpeg_cmd = [ + "ffmpeg", + "-i", + f"{args.filename}", + "-f", + "image2pipe", + "-vf", + f"fps={args.framerate},chromakey={args.chroma}", + "-pix_fmt", + "rgb24", + "-vcodec", + "rawvideo", + "-", + ] + else: + ffmpeg_cmd = [ + "ffmpeg", + "-i", + f"{args.filename}", + "-f", + "image2pipe", + "-vf", + f"fps={args.framerate}", + "-pix_fmt", + "rgb24", + "-vcodec", + "rawvideo", + "-", + ] + print_verbose(vid_width) + print_verbose(vid_height) + proc = subprocess.Popen(ffmpeg_cmd, stdout = subprocess.PIPE, stderr = stderr, bufsize=10**8) + frame_size = vid_width * vid_height * 3 + i=1 + while True: + raw_frame = proc.stdout.read(frame_size) + if not raw_frame: + break + ffmpeg_frame = np.frombuffer(raw_frame, np.uint8).reshape((vid_height, vid_width, 3)) f = str(i) + ".png" - path = BASE_PATH / "video" / f - - if is_valid_image(path): - thread_chafa = threading.Thread(target=chafa_process, args=(f, )) - thread_chafa.start() - threads.append(thread_chafa) - print_verbose("Launching chafa threads") - i = i + 1 - + thread_chafa = threading.Thread(target=chafa_process, args=(f, ffmpeg_frame )) + thread_chafa.start() + threads.append(thread_chafa) + print_verbose("Launching chafa thread") + i=i+1 + for i in enumerate(threads): thread_chafa.join() -thread_files = threading.Thread(target=chafa_files, args=(code, )) - -def ffmpeg_process(): - stdout = None if args.verbose else subprocess.DEVNULL - stderr = None if args.verbose else subprocess.STDOUT - if args.chroma_flag_given: - ffmpeg = subprocess.Popen( - [ - "ffmpeg", - "-i", - f"{args.filename}", - "-vf", - f"fps={args.framerate},format=rgba,chromakey={args.chroma}", - str(BASE_PATH / "video/%d.png"), - ], - stdout=stdout, - stderr=stderr, - ) - else: - ffmpeg = subprocess.Popen( - [ - "ffmpeg", - "-i", - f"{args.filename}", - "-vf", - f"fps={args.framerate},format=rgba", - str(BASE_PATH / "video/%d.png"), - ], - stdout=stdout, - stderr=stderr, - ) - ffmpeg.wait() - code.append("done") - thread_ffmpeg = threading.Thread(target=ffmpeg_process) @@ -345,8 +354,6 @@ def ffmpeg_process(): print_verbose("Emptied the output folder.") - thread_files.start() - thread_ffmpeg.start() thread_sound.start() @@ -356,9 +363,6 @@ def ffmpeg_process(): thread_sound.join() - thread_files.join() - - # get the frames else: # just use cached diff --git a/flake.nix b/flake.nix index 1d8853b..abb6e1c 100644 --- a/flake.nix +++ b/flake.nix @@ -31,6 +31,7 @@ packages = [ (pkgs.python3.withPackages (python-pkgs: [ python-pkgs.pillow + python-pkgs.numpy ])) pkgs.neofetch pkgs.fastfetch diff --git a/nix/packages/anifetch.nix b/nix/packages/anifetch.nix index 0fca694..466b5d4 100644 --- a/nix/packages/anifetch.nix +++ b/nix/packages/anifetch.nix @@ -21,6 +21,7 @@ in pkgs.chafa pkgs.ffmpeg pkgs.python3Packages.pillow + pkgs.python3Packages.numpy loop ]; preBuild = '' From 58fd823ff87b8584f141fe4fef97f62204d5e6d2 Mon Sep 17 00:00:00 2001 From: Immelancholy Date: Wed, 21 May 2025 15:35:45 +0100 Subject: [PATCH 14/15] Remove unnecessary commented function --- anifetch.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/anifetch.py b/anifetch.py index 3628186..fa222af 100644 --- a/anifetch.py +++ b/anifetch.py @@ -10,17 +10,6 @@ from PIL import Image import numpy as np -# def is_valid_image(file_path): -# if os.path.exists(file_path): -# try: -# im = Image.open(file_path) -# im.verify() -# return True -# except Exception or IOError: -# return False -# else: -# return False - def print_verbose(*msg): if args.verbose: print(*msg) @@ -358,7 +347,6 @@ def ffmpeg_process(): thread_sound.start() - thread_ffmpeg.join() thread_sound.join() From b73546b9eb4c01315f28c1c8b91764094c08e2b6 Mon Sep 17 00:00:00 2001 From: Immelancholy Date: Wed, 21 May 2025 16:13:23 +0100 Subject: [PATCH 15/15] Add this line too lol --- anifetch.py | 1 + 1 file changed, 1 insertion(+) diff --git a/anifetch.py b/anifetch.py index fa222af..16f5ca2 100644 --- a/anifetch.py +++ b/anifetch.py @@ -316,6 +316,7 @@ def ffmpeg_process(): if not raw_frame: break ffmpeg_frame = np.frombuffer(raw_frame, np.uint8).reshape((vid_height, vid_width, 3)) + proc.stdout.flush() f = str(i) + ".png" thread_chafa = threading.Thread(target=chafa_process, args=(f, ffmpeg_frame )) thread_chafa.start()