mirror of
https://github.com/caelestia-dots/cli.git
synced 2026-06-05 23:09:27 -05:00
feat: impl wallpaper
This commit is contained in:
@@ -1,7 +1,9 @@
|
|||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
from caelestia.subcommands import clipboard, emoji, pip, record, scheme, screenshot, shell, toggle, wallpaper, wsaction
|
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.scheme import get_scheme_names, scheme_variants
|
||||||
|
from caelestia.utils.wallpaper import get_wallpaper
|
||||||
|
|
||||||
|
|
||||||
def parse_args() -> (argparse.ArgumentParser, argparse.Namespace):
|
def parse_args() -> (argparse.ArgumentParser, argparse.Namespace):
|
||||||
@@ -81,14 +83,18 @@ def parse_args() -> (argparse.ArgumentParser, argparse.Namespace):
|
|||||||
# Create parser for wallpaper opts
|
# Create parser for wallpaper opts
|
||||||
wallpaper_parser = command_parser.add_parser("wallpaper", help="manage the wallpaper")
|
wallpaper_parser = command_parser.add_parser("wallpaper", help="manage the wallpaper")
|
||||||
wallpaper_parser.set_defaults(cls=wallpaper.Command)
|
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(
|
||||||
wallpaper_parser.add_argument("-r", "--random", action="store_true", help="switch to a random wallpaper")
|
"-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("-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("-n", "--no-filter", action="store_true", help="do not filter by size")
|
||||||
wallpaper_parser.add_argument(
|
wallpaper_parser.add_argument(
|
||||||
"-t",
|
"-t",
|
||||||
"--threshold",
|
"--threshold",
|
||||||
default=80,
|
default=0.8,
|
||||||
help="the minimum percentage of the largest monitor size the image must be greater than to be selected",
|
help="the minimum percentage of the largest monitor size the image must be greater than to be selected",
|
||||||
)
|
)
|
||||||
wallpaper_parser.add_argument(
|
wallpaper_parser.add_argument(
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
|
import json
|
||||||
from argparse import Namespace
|
from argparse import Namespace
|
||||||
|
|
||||||
|
from caelestia.utils.wallpaper import get_colours_for_wall, get_wallpaper, set_random, set_wallpaper
|
||||||
|
|
||||||
|
|
||||||
class Command:
|
class Command:
|
||||||
args: Namespace
|
args: Namespace
|
||||||
@@ -8,4 +11,11 @@ class Command:
|
|||||||
self.args = args
|
self.args = args
|
||||||
|
|
||||||
def run(self) -> None:
|
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())
|
||||||
|
|||||||
@@ -22,11 +22,14 @@ scheme_path = c_state_dir / "scheme.json"
|
|||||||
scheme_data_dir = cli_data_dir / "schemes"
|
scheme_data_dir = cli_data_dir / "schemes"
|
||||||
scheme_cache_dir = c_cache_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"
|
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()
|
sha = hashlib.sha256()
|
||||||
|
|
||||||
with open(path, "rb") as f:
|
with open(path, "rb") as f:
|
||||||
|
|||||||
@@ -59,8 +59,7 @@ class Scheme:
|
|||||||
|
|
||||||
self._flavour = flavour
|
self._flavour = flavour
|
||||||
self._check_mode()
|
self._check_mode()
|
||||||
self._update_colours()
|
self.update_colours()
|
||||||
self.save()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def mode(self) -> str:
|
def mode(self) -> str:
|
||||||
@@ -75,8 +74,7 @@ class Scheme:
|
|||||||
raise ValueError(f'Invalid scheme mode: "{mode}". Valid modes: {get_scheme_modes()}')
|
raise ValueError(f'Invalid scheme mode: "{mode}". Valid modes: {get_scheme_modes()}')
|
||||||
|
|
||||||
self._mode = mode
|
self._mode = mode
|
||||||
self._update_colours()
|
self.update_colours()
|
||||||
self.save()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def variant(self) -> str:
|
def variant(self) -> str:
|
||||||
@@ -88,8 +86,7 @@ class Scheme:
|
|||||||
return
|
return
|
||||||
|
|
||||||
self._variant = variant
|
self._variant = variant
|
||||||
self._update_colours()
|
self.update_colours()
|
||||||
self.save()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def colours(self) -> dict[str, str]:
|
def colours(self) -> dict[str, str]:
|
||||||
@@ -115,6 +112,9 @@ class Scheme:
|
|||||||
self._name = random.choice(get_scheme_names())
|
self._name = random.choice(get_scheme_names())
|
||||||
self._flavour = random.choice(get_scheme_flavours())
|
self._flavour = random.choice(get_scheme_flavours())
|
||||||
self._mode = random.choice(get_scheme_modes())
|
self._mode = random.choice(get_scheme_modes())
|
||||||
|
self.update_colours()
|
||||||
|
|
||||||
|
def update_colours(self) -> None:
|
||||||
self._update_colours()
|
self._update_colours()
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
Reference in New Issue
Block a user