forked from Shinonome/dots-hyprland
Rearrange for tidier structure (#2212)
This commit is contained in:
+128
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Generate thumbnails for files using ImageMagick, following Freedesktop spec
|
||||
# Usage:
|
||||
# ./generate-thumbnails-magick.sh --file <path>
|
||||
# ./generate-thumbnails-magick.sh --directory <path>
|
||||
|
||||
set -e
|
||||
|
||||
# Thumbnail sizes mapping
|
||||
get_thumbnail_size() {
|
||||
case "$1" in
|
||||
normal) echo 128 ;;
|
||||
large) echo 256 ;;
|
||||
x-large) echo 512 ;;
|
||||
xx-large) echo 1024 ;;
|
||||
*) echo 128 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 --file <path> | --directory <path>"
|
||||
exit 1
|
||||
}
|
||||
|
||||
md5() {
|
||||
# Calculate md5 hash of the file's absolute path
|
||||
echo -n "$1" | md5sum | awk '{print $1}'
|
||||
}
|
||||
|
||||
urlencode() {
|
||||
# Percent-encode a string for use in a URI, but do not encode slashes
|
||||
local str="$1"
|
||||
local encoded=""
|
||||
local c
|
||||
for ((i=0; i<${#str}; i++)); do
|
||||
c="${str:$i:1}"
|
||||
case "$c" in
|
||||
[a-zA-Z0-9.~_-]|/) encoded+="$c" ;;
|
||||
*) printf -v hex '%%%02X' "'${c}'"; encoded+="$hex" ;;
|
||||
esac
|
||||
done
|
||||
echo "$encoded"
|
||||
}
|
||||
|
||||
generate_thumbnail() {
|
||||
local src="$1"
|
||||
local abs_path
|
||||
abs_path="$(realpath "$src")"
|
||||
# Skip files with multiple frames (GIFs, videos, etc.)
|
||||
case "${abs_path,,}" in
|
||||
*.gif|*.mp4|*.webm|*.mkv|*.avi|*.mov)
|
||||
return
|
||||
;;
|
||||
esac
|
||||
local encoded_path
|
||||
encoded_path="$(urlencode "$abs_path")"
|
||||
local uri
|
||||
uri="file://$encoded_path"
|
||||
local hash
|
||||
hash="$(md5 "$uri")"
|
||||
local out="$CACHE_DIR/$hash.png"
|
||||
mkdir -p "$CACHE_DIR"
|
||||
if [ -f "$out" ]; then
|
||||
return
|
||||
fi
|
||||
magick "$abs_path" -resize "${THUMBNAIL_SIZE}x${THUMBNAIL_SIZE}" "$out"
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
SIZE_NAME="normal"
|
||||
MODE=""
|
||||
TARGET=""
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--file|-f)
|
||||
MODE="file"
|
||||
TARGET="$2"
|
||||
shift 2
|
||||
;;
|
||||
--directory|-d)
|
||||
MODE="dir"
|
||||
TARGET="$2"
|
||||
shift 2
|
||||
;;
|
||||
--size|-s)
|
||||
SIZE_NAME="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
# Only one mode allowed
|
||||
[[ -n "$MODE" ]] && break
|
||||
done
|
||||
|
||||
THUMBNAIL_SIZE="$(get_thumbnail_size "$SIZE_NAME")"
|
||||
CACHE_DIR="$HOME/.cache/thumbnails/$SIZE_NAME"
|
||||
|
||||
if [ -z "$MODE" ] || [ -z "$TARGET" ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
case "$MODE" in
|
||||
file)
|
||||
if [ ! -f "$TARGET" ]; then
|
||||
echo "File not found: $TARGET"
|
||||
exit 2
|
||||
fi
|
||||
generate_thumbnail "$TARGET"
|
||||
;;
|
||||
dir)
|
||||
if [ ! -d "$TARGET" ]; then
|
||||
echo "Directory not found: $TARGET"
|
||||
exit 2
|
||||
fi
|
||||
for f in "$TARGET"/*; do
|
||||
[ -f "$f" ] || continue
|
||||
generate_thumbnail "$f" &
|
||||
done
|
||||
wait
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
source $(eval echo $ILLOGICAL_IMPULSE_VIRTUAL_ENV)/bin/activate
|
||||
"$SCRIPT_DIR/thumbgen.py" "$@"
|
||||
deactivate
|
||||
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# From https://github.com/difference-engine/thumbnail-generator-ubuntu (MIT License)
|
||||
# Since the script is small and the maintainers seem inactive to accept my PR (#11) I decided to just copy it over.
|
||||
# When it gets merged and the python package gets updated we can just use it
|
||||
|
||||
import os
|
||||
import sys
|
||||
from multiprocessing import Pool
|
||||
from pathlib import Path
|
||||
from typing import List, Union
|
||||
|
||||
import click
|
||||
import gi
|
||||
from loguru import logger
|
||||
from tqdm import tqdm
|
||||
|
||||
gi.require_version("GnomeDesktop", "3.0")
|
||||
from gi.repository import Gio, GnomeDesktop # isort:skip
|
||||
|
||||
thumbnail_size_map = {
|
||||
"normal": GnomeDesktop.DesktopThumbnailSize.NORMAL,
|
||||
"large": GnomeDesktop.DesktopThumbnailSize.LARGE,
|
||||
"x-large": GnomeDesktop.DesktopThumbnailSize.XLARGE,
|
||||
"xx-large": GnomeDesktop.DesktopThumbnailSize.XXLARGE,
|
||||
}
|
||||
|
||||
factory = None
|
||||
logger.remove()
|
||||
logger.add(sys.stdout, level="INFO")
|
||||
logger.add("/tmp/thumbgen.log", level="DEBUG", rotation="100 MB")
|
||||
|
||||
def make_thumbnail(fpath: str) -> bool:
|
||||
mtime = os.path.getmtime(fpath)
|
||||
# Use Gio to determine the URI and mime type
|
||||
f = Gio.file_new_for_path(str(fpath))
|
||||
uri = f.get_uri()
|
||||
info = f.query_info("standard::content-type", Gio.FileQueryInfoFlags.NONE, None)
|
||||
mime_type = info.get_content_type()
|
||||
|
||||
if factory.lookup(uri, mtime) is not None:
|
||||
logger.debug("FRESH {}".format(uri))
|
||||
return False
|
||||
|
||||
if not factory.can_thumbnail(uri, mime_type, mtime):
|
||||
logger.debug("UNSUPPORTED {}".format(uri))
|
||||
return False
|
||||
|
||||
thumbnail = factory.generate_thumbnail(uri, mime_type)
|
||||
if thumbnail is None:
|
||||
logger.debug("ERROR {}".format(uri))
|
||||
return False
|
||||
|
||||
logger.debug("OK {}".format(uri))
|
||||
factory.save_thumbnail(thumbnail, uri, mtime)
|
||||
return True
|
||||
|
||||
|
||||
@logger.catch()
|
||||
def thumbnail_folder(*, dir_path: Path, workers: int, only_images: bool, recursive: bool, machine_progress: bool = False) -> None:
|
||||
all_files = get_all_files(dir_path=dir_path, recursive=recursive)
|
||||
if only_images:
|
||||
all_files = get_all_images(all_files=all_files)
|
||||
all_files = [str(fpath) for fpath in all_files]
|
||||
if machine_progress:
|
||||
completed = 0
|
||||
total = len(all_files)
|
||||
with Pool(processes=workers) as p:
|
||||
for result in p.imap(make_thumbnail, all_files):
|
||||
completed += 1
|
||||
print(f"PROGRESS {completed}/{total} FILE {all_files[completed-1]}")
|
||||
sys.stdout.flush()
|
||||
else:
|
||||
with Pool(processes=workers) as p:
|
||||
list(tqdm(p.imap(make_thumbnail, all_files), total=len(all_files)))
|
||||
|
||||
|
||||
def get_all_images(*, all_files: List[Path]) -> List[Path]:
|
||||
img_suffixes = [".jpg", ".jpeg", ".png", ".gif"]
|
||||
all_images = [fpath for fpath in all_files if fpath.suffix in img_suffixes]
|
||||
print("Found {} images".format(len(all_images)))
|
||||
return all_images
|
||||
|
||||
|
||||
def get_all_files(*, dir_path: Path, recursive: bool) -> List[Path]:
|
||||
if not (dir_path.exists() and dir_path.is_dir()):
|
||||
raise ValueError("{} doesn't exist or isn't a valid directory!".format(dir_path.resolve()))
|
||||
if recursive:
|
||||
all_files = dir_path.rglob("*")
|
||||
else:
|
||||
all_files = dir_path.glob("*")
|
||||
all_files = [fpath for fpath in all_files if fpath.is_file()]
|
||||
print("Found {} files in the directory: {}".format(len(all_files), dir_path.resolve()))
|
||||
return all_files
|
||||
|
||||
@click.command()
|
||||
@click.option(
|
||||
"-d", "--img_dirs", required=True, help='directories to generate thumbnails seperated by space, eg: "dir1/dir2 dir3"'
|
||||
)
|
||||
@click.option(
|
||||
"-s", "--size", default="normal", type=click.Choice(["normal", "large", "x-large", "xx-large"]), help="Thumbnail size: normal, large, x-large, xx-large"
|
||||
)
|
||||
@click.option("-w", "--workers", default=1, help="no of cpus to use for processing")
|
||||
@click.option(
|
||||
"-i", "--only_images", is_flag=True, default=False, help="Whether to only look for images to be thumbnailed"
|
||||
)
|
||||
@click.option("-r", "--recursive", is_flag=True, default=False, help="Whether to recursively look for files")
|
||||
@click.option("--machine_progress", is_flag=True, default=False, help="Print machine-readable progress lines instead of a progress bar")
|
||||
def main(img_dirs: str, size: str, workers: str, only_images: bool, recursive: bool, machine_progress: bool) -> None:
|
||||
img_dirs = [Path(img_dir) for img_dir in img_dirs.split()]
|
||||
global factory
|
||||
factory = GnomeDesktop.DesktopThumbnailFactory.new(thumbnail_size_map[size])
|
||||
for img_dir in img_dirs:
|
||||
thumbnail_folder(dir_path=img_dir, workers=workers, only_images=only_images, recursive=recursive, machine_progress=machine_progress)
|
||||
print("Thumbnail Generation Completed!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user