From 0822e2e92cf4f484f6797bc8562ac6f62aa1c394 Mon Sep 17 00:00:00 2001 From: benexl Date: Wed, 29 Jan 2025 19:47:56 +0300 Subject: [PATCH] feat: improve preview logic --- fastanime/cli/commands/anilist/downloads.py | 8 +- fastanime/cli/commands/downloads.py | 8 +- fastanime/cli/interfaces/utils.py | 168 ++++++++++++-------- fastanime/cli/utils/scripts.py | 24 ++- 4 files changed, 132 insertions(+), 76 deletions(-) diff --git a/fastanime/cli/commands/anilist/downloads.py b/fastanime/cli/commands/anilist/downloads.py index e02d04f..7d4f634 100644 --- a/fastanime/cli/commands/anilist/downloads.py +++ b/fastanime/cli/commands/anilist/downloads.py @@ -124,7 +124,7 @@ def downloads( return from ....constants import APP_CACHE_DIR - from ...utils.scripts import fzf_preview + from ...utils.scripts import bash_functions downloads_thumbnail_cache_dir = os.path.join(APP_CACHE_DIR, "video_thumbnails") Path(downloads_thumbnail_cache_dir).mkdir(parents=True, exist_ok=True) @@ -183,7 +183,7 @@ def downloads( else echo Loading... fi """ % ( - fzf_preview, + bash_functions, downloads_thumbnail_cache_dir, downloads_thumbnail_cache_dir, ) @@ -194,7 +194,7 @@ def downloads( from pathlib import Path from ....constants import APP_CACHE_DIR - from ...utils.scripts import fzf_preview + from ...utils.scripts import bash_functions if not shutil.which("ffmpegthumbnailer"): print("ffmpegthumbnailer not found") @@ -256,7 +256,7 @@ def downloads( else echo Loading... fi """ % ( - fzf_preview, + bash_functions, downloads_thumbnail_cache_dir, downloads_thumbnail_cache_dir, ) diff --git a/fastanime/cli/commands/downloads.py b/fastanime/cli/commands/downloads.py index 7d71a1d..96d8272 100644 --- a/fastanime/cli/commands/downloads.py +++ b/fastanime/cli/commands/downloads.py @@ -124,7 +124,7 @@ def downloads( return from ...constants import APP_CACHE_DIR - from ..utils.scripts import fzf_preview + from ..utils.scripts import bash_functions downloads_thumbnail_cache_dir = os.path.join(APP_CACHE_DIR, "video_thumbnails") Path(downloads_thumbnail_cache_dir).mkdir(parents=True, exist_ok=True) @@ -183,7 +183,7 @@ def downloads( else echo Loading... fi """ % ( - fzf_preview, + bash_functions, downloads_thumbnail_cache_dir, downloads_thumbnail_cache_dir, ) @@ -194,7 +194,7 @@ def downloads( from pathlib import Path from ...constants import APP_CACHE_DIR - from ..utils.scripts import fzf_preview + from ..utils.scripts import bash_functions if not shutil.which("ffmpegthumbnailer"): print("ffmpegthumbnailer not found") @@ -256,7 +256,7 @@ def downloads( else echo Loading... fi """ % ( - fzf_preview, + bash_functions, downloads_thumbnail_cache_dir, downloads_thumbnail_cache_dir, ) diff --git a/fastanime/cli/interfaces/utils.py b/fastanime/cli/interfaces/utils.py index e86d414..fc7ea7e 100644 --- a/fastanime/cli/interfaces/utils.py +++ b/fastanime/cli/interfaces/utils.py @@ -5,14 +5,14 @@ import shutil import subprocess import textwrap from threading import Thread +from hashlib import sha256 import requests -from yt_dlp.utils import clean_html, sanitize_filename - +from yt_dlp.utils import clean_html from ...constants import APP_CACHE_DIR, S_PLATFORM from ...libs.anilist.types import AnilistBaseMediaDataSchema from ...Utility import anilist_data_helper -from ..utils.scripts import fzf_preview +from ..utils.scripts import bash_functions from ..utils.utils import get_true_fg, which_bashlike logger = logging.getLogger(__name__) @@ -69,7 +69,12 @@ def save_image_from_url(url: str, file_name: str): file_name: filename to use """ image = requests.get(url) - with open(os.path.join(IMAGES_CACHE_DIR, f"{file_name}.png"), "wb") as f: + with open( + os.path.join( + IMAGES_CACHE_DIR, f"{sha256(file_name.encode('utf-8')).hexdigest()}.png" + ), + "wb", + ) as f: f.write(image.content) @@ -83,7 +88,7 @@ def save_info_from_str(info: str, file_name: str): with open( os.path.join( ANIME_INFO_CACHE_DIR, - file_name, + sha256(file_name.encode("utf-8")).hexdigest(), ), "w", encoding="utf-8", @@ -108,11 +113,21 @@ def write_search_results( future_to_task = {} for anime, title in zip(anilist_results, titles): # actual image url + image_url = "" if os.environ.get("FASTANIME_IMAGE_PREVIEWS", "true").lower() == "true": image_url = anime["coverImage"]["large"] - future_to_task[ - executor.submit(save_image_from_url, image_url, title) - ] = image_url + + if not ( + os.path.exists( + os.path.join( + IMAGES_CACHE_DIR, + f"{sha256(title.encode('utf-8')).hexdigest()}.png", + ) + ) + ): + future_to_task[ + executor.submit(save_image_from_url, image_url, title) + ] = image_url mediaListName = "Not in any of your lists" progress = "UNKNOWN" @@ -121,54 +136,55 @@ def write_search_results( progress = anime_list["progress"] # handle the text data template = f""" + image_url={image_url} ll=2 while [ $ll -le $FZF_PREVIEW_COLUMNS ];do - echo -n -e "{get_true_fg("─",*SEPARATOR_COLOR,bold=False)}" + echo -n -e "{get_true_fg("─", *SEPARATOR_COLOR, bold=False)}" ((ll++)) done echo - echo "{get_true_fg('Title(jp):',*HEADER_COLOR)} {(anime['title']['romaji'] or "").replace('"',SINGLE_QUOTE)}" - echo "{get_true_fg('Title(eng):',*HEADER_COLOR)} {(anime['title']['english'] or "").replace('"',SINGLE_QUOTE)}" + echo "{get_true_fg("Title(jp):", *HEADER_COLOR)} {(anime["title"]["romaji"] or "").replace('"', SINGLE_QUOTE)}" + echo "{get_true_fg("Title(eng):", *HEADER_COLOR)} {(anime["title"]["english"] or "").replace('"', SINGLE_QUOTE)}" ll=2 while [ $ll -le $FZF_PREVIEW_COLUMNS ];do - echo -n -e "{get_true_fg("─",*SEPARATOR_COLOR,bold=False)}" + echo -n -e "{get_true_fg("─", *SEPARATOR_COLOR, bold=False)}" ((ll++)) done echo - echo "{get_true_fg('Popularity:',*HEADER_COLOR)} {anilist_data_helper.format_number_with_commas(anime['popularity'])}" - echo "{get_true_fg('Favourites:',*HEADER_COLOR)} {anilist_data_helper.format_number_with_commas(anime['favourites'])}" - echo "{get_true_fg('Status:',*HEADER_COLOR)} {str(anime['status']).replace('"',SINGLE_QUOTE)}" - echo "{get_true_fg('Next Episode:',*HEADER_COLOR)} {anilist_data_helper.extract_next_airing_episode(anime['nextAiringEpisode']).replace('"',SINGLE_QUOTE)}" - echo "{get_true_fg('Genres:',*HEADER_COLOR)} {anilist_data_helper.format_list_data_with_comma(anime['genres']).replace('"',SINGLE_QUOTE)}" + echo "{get_true_fg("Popularity:", *HEADER_COLOR)} {anilist_data_helper.format_number_with_commas(anime["popularity"])}" + echo "{get_true_fg("Favourites:", *HEADER_COLOR)} {anilist_data_helper.format_number_with_commas(anime["favourites"])}" + echo "{get_true_fg("Status:", *HEADER_COLOR)} {str(anime["status"]).replace('"', SINGLE_QUOTE)}" + echo "{get_true_fg("Next Episode:", *HEADER_COLOR)} {anilist_data_helper.extract_next_airing_episode(anime["nextAiringEpisode"]).replace('"', SINGLE_QUOTE)}" + echo "{get_true_fg("Genres:", *HEADER_COLOR)} {anilist_data_helper.format_list_data_with_comma(anime["genres"]).replace('"', SINGLE_QUOTE)}" ll=2 while [ $ll -le $FZF_PREVIEW_COLUMNS ];do - echo -n -e "{get_true_fg("─",*SEPARATOR_COLOR,bold=False)}" + echo -n -e "{get_true_fg("─", *SEPARATOR_COLOR, bold=False)}" ((ll++)) done echo - echo "{get_true_fg('Episodes:',*HEADER_COLOR)} {(anime['episodes']) or 'UNKNOWN'}" - echo "{get_true_fg('Start Date:',*HEADER_COLOR)} {anilist_data_helper.format_anilist_date_object(anime['startDate']).replace('"',SINGLE_QUOTE)}" - echo "{get_true_fg('End Date:',*HEADER_COLOR)} {anilist_data_helper.format_anilist_date_object(anime['endDate']).replace('"',SINGLE_QUOTE)}" + echo "{get_true_fg("Episodes:", *HEADER_COLOR)} {(anime["episodes"]) or "UNKNOWN"}" + echo "{get_true_fg("Start Date:", *HEADER_COLOR)} {anilist_data_helper.format_anilist_date_object(anime["startDate"]).replace('"', SINGLE_QUOTE)}" + echo "{get_true_fg("End Date:", *HEADER_COLOR)} {anilist_data_helper.format_anilist_date_object(anime["endDate"]).replace('"', SINGLE_QUOTE)}" ll=2 while [ $ll -le $FZF_PREVIEW_COLUMNS ];do - echo -n -e "{get_true_fg("─",*SEPARATOR_COLOR,bold=False)}" + echo -n -e "{get_true_fg("─", *SEPARATOR_COLOR, bold=False)}" ((ll++)) done echo - echo "{get_true_fg('Media List:',*HEADER_COLOR)} {mediaListName.replace('"',SINGLE_QUOTE)}" - echo "{get_true_fg('Progress:',*HEADER_COLOR)} {progress}" + echo "{get_true_fg("Media List:", *HEADER_COLOR)} {mediaListName.replace('"', SINGLE_QUOTE)}" + echo "{get_true_fg("Progress:", *HEADER_COLOR)} {progress}" ll=2 while [ $ll -le $FZF_PREVIEW_COLUMNS ];do - echo -n -e "{get_true_fg("─",*SEPARATOR_COLOR,bold=False)}" + echo -n -e "{get_true_fg("─", *SEPARATOR_COLOR, bold=False)}" ((ll++)) done echo - # echo "{get_true_fg('Description:',*HEADER_COLOR).replace('"',SINGLE_QUOTE)}" + # echo "{get_true_fg("Description:", *HEADER_COLOR).replace('"', SINGLE_QUOTE)}" """ template = textwrap.dedent(template) template = f""" {template} - echo "{textwrap.fill(clean_html((anime['description']) or "").replace('"',SINGLE_QUOTE), width=45)}" + echo "{textwrap.fill(clean_html((anime["description"]) or "").replace('"', SINGLE_QUOTE), width=45)}" """ future_to_task[executor.submit(save_info_from_str, template, title)] = title @@ -199,9 +215,18 @@ def get_rofi_icons( for anime, title in zip(anilist_results, titles): # actual link to download image from image_url = anime["coverImage"]["large"] - future_to_url[executor.submit(save_image_from_url, image_url, title)] = ( - image_url - ) + + if not ( + os.path.exists( + os.path.join( + IMAGES_CACHE_DIR, + f"{sha256(title.encode('utf-8')).hexdigest()}.png", + ) + ) + ): + future_to_url[ + executor.submit(save_image_from_url, image_url, title) + ] = image_url # execute the jobs for future in concurrent.futures.as_completed(future_to_url): @@ -229,13 +254,22 @@ def get_fzf_manga_preview(manga_results, workers=None, wait=False): future_to_url = {} for manga in manga_results: image_url = manga["poster"] - future_to_url[ - executor.submit( - save_image_from_url, - image_url, - sanitize_filename(manga["title"]), + + if not ( + os.path.exists( + os.path.join( + IMAGES_CACHE_DIR, + f"{sha256(manga['title'].encode('utf-8')).hexdigest()}.png", + ) ) - ] = image_url + ): + future_to_url[ + executor.submit( + save_image_from_url, + image_url, + manga["title"], + ) + ] = image_url # execute the jobs for future in concurrent.futures.as_completed(future_to_url): @@ -256,11 +290,13 @@ def get_fzf_manga_preview(manga_results, workers=None, wait=False): os.environ["SHELL"] = shutil.which("bash") or "bash" preview = """ %s - if [ -s %s/{} ]; then fzf-preview %s/{} + title="$(echo -n {})" + title="$(echo -n "$title" |generate_sha256)" + if [ -s "%s/$title" ]; then fzf_preview "%s/title" else echo Loading... fi """ % ( - fzf_preview, + bash_functions, IMAGES_CACHE_DIR, IMAGES_CACHE_DIR, ) @@ -303,27 +339,29 @@ def get_fzf_episode_preview( if episode_title and image_url: future_to_url[ - executor.submit(save_image_from_url, image_url, episode) + executor.submit(save_image_from_url, image_url, str(episode)) ] = image_url template = textwrap.dedent( f""" ll=2 while [ $ll -le $FZF_PREVIEW_COLUMNS ];do - echo -n -e "{get_true_fg("─",*SEPARATOR_COLOR,bold=False)}" + echo -n -e "{get_true_fg("─", *SEPARATOR_COLOR, bold=False)}" ((ll++)) done - echo "{get_true_fg('Anime Title(eng):',*HEADER_COLOR)} {('' or anilist_result['title']['english']).replace('"',SINGLE_QUOTE)}" - echo "{get_true_fg('Anime Title(jp):',*HEADER_COLOR)} {(anilist_result['title']['romaji'] or '').replace('"',SINGLE_QUOTE)}" + echo + echo "{get_true_fg("Anime Title(eng):", *HEADER_COLOR)} {("" or anilist_result["title"]["english"]).replace('"', SINGLE_QUOTE)}" + echo "{get_true_fg("Anime Title(jp):", *HEADER_COLOR)} {(anilist_result["title"]["romaji"] or "").replace('"', SINGLE_QUOTE)}" ll=2 while [ $ll -le $FZF_PREVIEW_COLUMNS ];do - echo -n -e "{get_true_fg("─",*SEPARATOR_COLOR,bold=False)}" + echo -n -e "{get_true_fg("─", *SEPARATOR_COLOR, bold=False)}" ((ll++)) done - echo "{str(episode_title).replace('"',SINGLE_QUOTE)}" + echo + echo "{str(episode_title).replace('"', SINGLE_QUOTE)}" ll=2 while [ $ll -le $FZF_PREVIEW_COLUMNS ];do - echo -n -e "{get_true_fg("─",*SEPARATOR_COLOR,bold=False)}" + echo -n -e "{get_true_fg("─", *SEPARATOR_COLOR, bold=False)}" ((ll++)) done """ @@ -356,10 +394,10 @@ def get_fzf_episode_preview( if S_PLATFORM == "win32": preview = """ %s - title={} - show_image_previews="%s" + title="$(echo -n {})" + title="$(echo -n "$title" |generate_sha256)" dim=${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES} - if [ $show_image_previews = "true" ];then + if [ "$FASTANIME_IMAGE_PREVIEWS" = "True" ];then if [ -s "%s\\\\\\${title}.png" ]; then if command -v "chafa">/dev/null;then chafa -s $dim "%s\\\\\\${title}.png" @@ -375,8 +413,7 @@ def get_fzf_episode_preview( else echo Loading... fi """ % ( - fzf_preview, - os.environ.get("FASTANIME_IMAGE_PREVIEWS", "true").lower(), + bash_functions, IMAGES_CACHE_DIR.replace("\\", "\\\\\\"), IMAGES_CACHE_DIR.replace("\\", "\\\\\\"), ANIME_INFO_CACHE_DIR.replace("\\", "\\\\\\"), @@ -384,11 +421,11 @@ def get_fzf_episode_preview( ) else: preview = """ - title={} %s - show_image_previews="%s" - if [ $show_image_previews = "true" ];then - if [ -s %s/${title}.png ]; then fzf-preview %s/${title}.png + title="$(echo -n {})" + title="$(echo -n "$title" |generate_sha256)" + if [ "$FASTANIME_IMAGE_PREVIEWS" = "True" ];then + if [ -s %s/${title}.png ]; then fzf_preview %s/${title}.png else echo Loading... fi fi @@ -396,8 +433,7 @@ def get_fzf_episode_preview( else echo Loading... fi """ % ( - fzf_preview, - os.environ.get("FASTANIME_IMAGE_PREVIEWS", "true").lower(), + bash_functions, IMAGES_CACHE_DIR, IMAGES_CACHE_DIR, ANIME_INFO_CACHE_DIR, @@ -439,10 +475,10 @@ def get_fzf_anime_preview( if S_PLATFORM == "win32": preview = """ %s - title={} - show_image_previews="%s" + title="$(echo -n {})" + title="$(echo -n "$title" |generate_sha256)" dim=${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES} - if [ $show_image_previews = "true" ];then + if [ "$FASTANIME_IMAGE_PREVIEWS" = "True" ];then if [ -s "%s\\\\\\${title}.png" ]; then if command -v "chafa">/dev/null;then chafa -s $dim "%s\\\\\\${title}.png" @@ -458,8 +494,7 @@ def get_fzf_anime_preview( else echo Loading... fi """ % ( - fzf_preview, - os.environ.get("FASTANIME_IMAGE_PREVIEWS", "true").lower(), + bash_functions, IMAGES_CACHE_DIR.replace("\\", "\\\\\\"), IMAGES_CACHE_DIR.replace("\\", "\\\\\\"), ANIME_INFO_CACHE_DIR.replace("\\", "\\\\\\"), @@ -468,10 +503,10 @@ def get_fzf_anime_preview( else: preview = """ %s - title={} - show_image_previews="%s" - if [ $show_image_previews = "true" ];then - if [ -s "%s/${title}.png" ]; then fzf-preview "%s/${title}.png" + title="$(echo -n {})" + title="$(echo -n "$title" |generate_sha256)" + if [ "$FASTANIME_IMAGE_PREVIEWS" = "True" ];then + if [ -s "%s/${title}.png" ]; then fzf_preview "%s/${title}.png" else echo Loading... fi fi @@ -479,8 +514,7 @@ def get_fzf_anime_preview( else echo Loading... fi """ % ( - fzf_preview, - os.environ.get("FASTANIME_IMAGE_PREVIEWS", "true").lower(), + bash_functions, IMAGES_CACHE_DIR, IMAGES_CACHE_DIR, ANIME_INFO_CACHE_DIR, diff --git a/fastanime/cli/utils/scripts.py b/fastanime/cli/utils/scripts.py index 57f81a0..92bd5b6 100644 --- a/fastanime/cli/utils/scripts.py +++ b/fastanime/cli/utils/scripts.py @@ -1,4 +1,26 @@ -fzf_preview = r""" +bash_functions = r""" +generate_sha256() { + local input + + # Check if input is passed as an argument or piped + if [ -n "$1" ]; then + input="$1" + else + input=$(cat) + fi + + if command -v sha256sum &>/dev/null; then + echo -n "$input" | sha256sum | awk '{print $1}' + elif command -v shasum &>/dev/null; then + echo -n "$input" | shasum -a 256 | awk '{print $1}' + elif command -v sha256 &>/dev/null; then + echo -n "$input" | sha256 | awk '{print $1}' + elif command -v openssl &>/dev/null; then + echo -n "$input" | openssl dgst -sha256 | awk '{print $2}' + else + echo -n "$input" | base64 | tr '/+' '_-' | tr -d '\n' + fi +} fzf_preview() { file=$1