diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/caelestia/parser.py | 12 | ||||
| -rw-r--r-- | src/caelestia/subcommands/wallpaper.py | 12 | ||||
| -rw-r--r-- | src/caelestia/utils/paths.py | 7 | ||||
| -rw-r--r-- | src/caelestia/utils/scheme.py | 12 | ||||
| -rw-r--r-- | src/caelestia/utils/wallpaper.py | 123 |
5 files changed, 154 insertions, 12 deletions
diff --git a/src/caelestia/parser.py b/src/caelestia/parser.py index 3f6f506..f8c7bac 100644 --- a/src/caelestia/parser.py +++ b/src/caelestia/parser.py @@ -1,7 +1,9 @@ import argparse from caelestia.subcommands import clipboard, emoji, pip, record, scheme, screenshot, shell, toggle, wallpaper, wsaction +from caelestia.utils.paths import wallpapers_dir from caelestia.utils.scheme import get_scheme_names, scheme_variants +from caelestia.utils.wallpaper import get_wallpaper def parse_args() -> (argparse.ArgumentParser, argparse.Namespace): @@ -81,14 +83,18 @@ def parse_args() -> (argparse.ArgumentParser, argparse.Namespace): # Create parser for wallpaper opts wallpaper_parser = command_parser.add_parser("wallpaper", help="manage the wallpaper") wallpaper_parser.set_defaults(cls=wallpaper.Command) - wallpaper_parser.add_argument("-g", "--get", action="store_true", help="print the current wallpaper") - wallpaper_parser.add_argument("-r", "--random", action="store_true", help="switch to a random wallpaper") + wallpaper_parser.add_argument( + "-p", "--print", nargs="?", const=get_wallpaper(), metavar="PATH", help="print the scheme for a wallpaper" + ) + wallpaper_parser.add_argument( + "-r", "--random", nargs="?", const=wallpapers_dir, metavar="DIR", help="switch to a random wallpaper" + ) wallpaper_parser.add_argument("-f", "--file", help="the path to the wallpaper to switch to") wallpaper_parser.add_argument("-n", "--no-filter", action="store_true", help="do not filter by size") wallpaper_parser.add_argument( "-t", "--threshold", - default=80, + default=0.8, help="the minimum percentage of the largest monitor size the image must be greater than to be selected", ) wallpaper_parser.add_argument( diff --git a/src/caelestia/subcommands/wallpaper.py b/src/caelestia/subcommands/wallpaper.py index 37f9a2b..1440484 100644 --- a/src/caelestia/subcommands/wallpaper.py +++ b/src/caelestia/subcommands/wallpaper.py @@ -1,5 +1,8 @@ +import json from argparse import Namespace +from caelestia.utils.wallpaper import get_colours_for_wall, get_wallpaper, set_random, set_wallpaper + class Command: args: Namespace @@ -8,4 +11,11 @@ class Command: self.args = args def run(self) -> None: - pass + if self.args.print: + print(json.dumps(get_colours_for_wall(self.args.print, self.args.no_smart))) + elif self.args.file: + set_wallpaper(self.args.file, self.args.no_smart) + elif self.args.random: + set_random(self.args) + else: + print(get_wallpaper()) diff --git a/src/caelestia/utils/paths.py b/src/caelestia/utils/paths.py index 3b5a7a6..f81b996 100644 --- a/src/caelestia/utils/paths.py +++ b/src/caelestia/utils/paths.py @@ -22,11 +22,14 @@ scheme_path = c_state_dir / "scheme.json" scheme_data_dir = cli_data_dir / "schemes" scheme_cache_dir = c_cache_dir / "schemes" -last_wallpaper_path = c_state_dir / "wallpaper/last.txt" +wallpapers_dir = Path.home() / "Pictures/Wallpapers" +wallpaper_path_path = c_state_dir / "wallpaper/path.txt" +wallpaper_link_path = c_state_dir / "wallpaper/current" wallpaper_thumbnail_path = c_state_dir / "wallpaper/thumbnail.jpg" +thumbnail_cache_dir = c_cache_dir / "thumbnails" -def compute_hash(path: str) -> str: +def compute_hash(path: Path | str) -> str: sha = hashlib.sha256() with open(path, "rb") as f: diff --git a/src/caelestia/utils/scheme.py b/src/caelestia/utils/scheme.py index 9027589..0d6cfb5 100644 --- a/src/caelestia/utils/scheme.py +++ b/src/caelestia/utils/scheme.py @@ -59,8 +59,7 @@ class Scheme: self._flavour = flavour self._check_mode() - self._update_colours() - self.save() + self.update_colours() @property def mode(self) -> str: @@ -75,8 +74,7 @@ class Scheme: raise ValueError(f'Invalid scheme mode: "{mode}". Valid modes: {get_scheme_modes()}') self._mode = mode - self._update_colours() - self.save() + self.update_colours() @property def variant(self) -> str: @@ -88,8 +86,7 @@ class Scheme: return self._variant = variant - self._update_colours() - self.save() + self.update_colours() @property def colours(self) -> dict[str, str]: @@ -115,6 +112,9 @@ class Scheme: self._name = random.choice(get_scheme_names()) self._flavour = random.choice(get_scheme_flavours()) self._mode = random.choice(get_scheme_modes()) + self.update_colours() + + def update_colours(self) -> None: self._update_colours() self.save() diff --git a/src/caelestia/utils/wallpaper.py b/src/caelestia/utils/wallpaper.py new file mode 100644 index 0000000..c8b3a72 --- /dev/null +++ b/src/caelestia/utils/wallpaper.py @@ -0,0 +1,123 @@ +import random +from argparse import Namespace +from pathlib import Path + +from materialyoucolor.hct import Hct +from materialyoucolor.utils.color_utils import argb_from_rgb +from PIL import Image + +from caelestia.utils.hypr import message +from caelestia.utils.material import get_colours_for_image +from caelestia.utils.paths import ( + compute_hash, + thumbnail_cache_dir, + wallpaper_link_path, + wallpaper_path_path, + wallpaper_thumbnail_path, +) +from caelestia.utils.scheme import Scheme, get_scheme +from caelestia.utils.theme import apply_colours + + +def is_valid_image(path: Path | str) -> bool: + path = Path(path) + return path.is_file() and path.suffix in [".jpg", ".jpeg", ".png", ".webp", ".tif", ".tiff"] + + +def check_wall(wall: Path, filter_size: tuple[int, int], threshold: float) -> bool: + with Image.open(wall) as img: + width, height = img.size + return width >= filter_size[0] * threshold and height >= filter_size[1] * threshold + + +def get_wallpaper() -> str: + return wallpaper_path_path.read_text() + + +def get_wallpapers(args: Namespace) -> list[Path]: + dir = Path(args.random) + if not dir.is_dir(): + return [] + + walls = [f for f in dir.rglob("*") if is_valid_image(f)] + + if args.no_filter: + return walls + + monitors = message("monitors") + filter_size = monitors[0]["width"], monitors[0]["height"] + for monitor in monitors[1:]: + if filter_size[0] > monitor["width"]: + filter_size[0] = monitor["width"] + if filter_size[1] > monitor["height"]: + filter_size[1] = monitor["height"] + + return [f for f in walls if check_wall(f, filter_size, args.threshold)] + + +def get_thumb(wall: Path) -> Path: + thumb = (thumbnail_cache_dir / compute_hash(wall)).with_suffix(".jpg") + + if not thumb.exists(): + with Image.open(wall) as img: + img = img.convert("RGB") + img.thumbnail((128, 128), Image.NEAREST) + thumb.parent.mkdir(parents=True, exist_ok=True) + img.save(thumb, "JPEG") + + return thumb + + +def get_smart_mode(wall: Path) -> str: + with Image.open(get_thumb(wall)) as img: + img.thumbnail((1, 1), Image.LANCZOS) + tone = Hct.from_int(argb_from_rgb(*img.getpixel((0, 0)))).tone + return "light" if tone > 60 else "dark" + + +def get_colours_for_wall(wall: Path | str, no_smart: bool) -> None: + scheme = get_scheme() + + if not no_smart: + scheme = Scheme( + { + "name": scheme.name, + "flavour": scheme.flavour, + "mode": get_smart_mode(wall), + "variant": scheme.variant, + "colours": scheme.colours, + } + ) + + return get_colours_for_image(get_thumb(wall), scheme) + + +def set_wallpaper(wall: Path | str, no_smart: bool) -> None: + if not is_valid_image(wall): + raise ValueError(f'"{wall}" is not a valid image') + + # Update files + wallpaper_path_path.parent.mkdir(parents=True, exist_ok=True) + wallpaper_path_path.write_text(str(wall)) + wallpaper_link_path.parent.mkdir(parents=True, exist_ok=True) + wallpaper_link_path.unlink(missing_ok=True) + wallpaper_link_path.symlink_to(wall) + + # Generate thumbnail or get from cache + thumb = get_thumb(wall) + wallpaper_thumbnail_path.parent.mkdir(parents=True, exist_ok=True) + wallpaper_thumbnail_path.unlink(missing_ok=True) + wallpaper_thumbnail_path.symlink_to(thumb) + + scheme = get_scheme() + + # Change mode based on wallpaper colour + scheme.mode = get_smart_mode(wall) + + # Update colours + scheme.update_colours() + apply_colours(scheme.colours, scheme.mode) + + +def set_random(args: Namespace) -> None: + set_wallpaper(random.choice(get_wallpapers(args)), args.no_smart) |