Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions src/caelestia/utils/paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,41 @@
import os
import shutil
import tempfile
from dataclasses import dataclass
from pathlib import Path
from typing import Any


@dataclass(frozen=True)
class WallpaperPaths:
base: Path
path: Path
current: Path
thumbnail: Path


def resolve_wallpaper_paths(monitor: str | None) -> WallpaperPaths:
base = c_state_dir / "wallpapers" / (monitor or "default")
return WallpaperPaths(
base=base,
path=base / "path.txt",
current=base / "current",
thumbnail=base / "thumbnail.jpg",
)


# TODO: Perhaps shift to using this for all the paths? example:
"""
@dataclass(frozen=True)
class XDGDirs:
config: Path = Path(os.getenv("XDG_CONFIG_HOME", Path.home() / ".config"))
data: Path = Path(os.getenv("XDG_DATA_HOME", Path.home() / ".local/share"))
state: Path = Path(os.getenv("XDG_STATE_HOME", Path.home() / ".local/state"))
cache: Path = Path(os.getenv("XDG_CACHE_HOME", Path.home() / ".cache"))
pictures: Path = Path(os.getenv("XDG_PICTURES_DIR", Path.home() / "Pictures"))
videos: Path = Path(os.getenv("XDG_VIDEOS_DIR", Path.home() / "Videos"))
"""

config_dir: Path = Path(os.getenv("XDG_CONFIG_HOME", Path.home() / ".config"))
data_dir: Path = Path(os.getenv("XDG_DATA_HOME", Path.home() / ".local/share"))
state_dir: Path = Path(os.getenv("XDG_STATE_HOME", Path.home() / ".local/state"))
Expand All @@ -29,6 +61,7 @@
scheme_cache_dir: Path = c_cache_dir / "schemes"

wallpapers_dir: Path = Path(os.getenv("CAELESTIA_WALLPAPERS_DIR", pictures_dir / "Wallpapers"))

wallpaper_path_path: Path = c_state_dir / "wallpaper/path.txt"
wallpaper_link_path: Path = c_state_dir / "wallpaper/current"
wallpaper_thumbnail_path: Path = c_state_dir / "wallpaper/thumbnail.jpg"
Expand Down
118 changes: 77 additions & 41 deletions src/caelestia/utils/wallpaper.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
from caelestia.utils.material import get_colours_for_image
from caelestia.utils.colourfulness import get_variant
from caelestia.utils.paths import (
WallpaperPaths,
resolve_wallpaper_paths,
compute_hash,
user_config_path,
wallpaper_link_path,
Expand Down Expand Up @@ -148,55 +150,89 @@ def convert_gif(wall: Path) -> Path:
return output_path


def set_wallpaper(wall: Path, no_smart: bool) -> None:
# Make path absolute
wall = Path(wall).resolve()
# TODO: Perhaps place somewhere else:
# START =============================

if not is_valid_image(wall):
raise ValueError(f'"{wall}" is not a valid image')

# Use gif's 1st frame for thumb only
wall_cache = convert_gif(wall) if wall.suffix.lower() == ".gif" else wall

# 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)
def check_monitor_exists(monitorName: str) -> bool:
monitors = cast(list[dict[str, int]], message("monitors"))
for monitor in monitors:
if monitor.get("name") == monitorName:
return True
return False

cache = wallpapers_cache_dir / compute_hash(wall_cache)

# Generate thumbnail or get from cache
thumb = get_thumb(wall_cache, cache)
wallpaper_thumbnail_path.parent.mkdir(parents=True, exist_ok=True)
wallpaper_thumbnail_path.unlink(missing_ok=True)
wallpaper_thumbnail_path.symlink_to(thumb)
# ============================= END

scheme = get_scheme()

# Change mode and variant based on wallpaper colour
if scheme.name == "dynamic" and not no_smart:
smart_opts = get_smart_opts(wall_cache, cache)
scheme.mode = smart_opts["mode"]
scheme.variant = smart_opts["variant"]
def set_wallpaper(wall: Path, no_smart: bool, monitor: str | None = None) -> None:
# Make path absolute
wall = Path(wall).resolve()

# Update colours
scheme.update_colours()
apply_colours(scheme.colours, scheme.mode)
if not is_valid_image(wall):
raise ValueError(f'"{wall}" is not a valid image')

# Run custom post-hook if configured
try:
cfg = json.loads(user_config_path.read_text()).get("wallpaper", {})
if post_hook := cfg.get("postHook"):
subprocess.run(
post_hook,
shell=True,
env={**os.environ, "WALLPAPER_PATH": str(wall)},
stderr=subprocess.DEVNULL,
)
except (FileNotFoundError, json.JSONDecodeError):
pass
if monitor is not None and check_monitor_exists(monitor):
wall_cache = convert_gif(wall) if wall.suffix.lower() == ".gif" else wall
cache = wallpapers_cache_dir / compute_hash(wall_cache)
thumb = get_thumb(wall_cache, cache)

paths = resolve_wallpaper_paths(monitor)
paths.base.mkdir(parents=True, exist_ok=True)
paths.path.write_text(str(wall))
paths.current.unlink(missing_ok=True)
paths.current.symlink_to(wall)
paths.thumbnail.unlink(missing_ok=True)
paths.thumbnail.symlink_to(thumb)

# TODO: Insert code for dynamic color gen here.
# Base it on wallpaper on primary monitor?
# Base it on all wallpapers for all monitors?

# HACK: Keep for backwards compatibility:
else:
# Use gif's 1st frame for thumb only
wall_cache = convert_gif(wall) if wall.suffix.lower() == ".gif" else wall

# 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)

cache = wallpapers_cache_dir / compute_hash(wall_cache)

# Generate thumbnail or get from cache
thumb = get_thumb(wall_cache, cache)
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 and variant based on wallpaper colour
if scheme.name == "dynamic" and not no_smart:
smart_opts = get_smart_opts(wall_cache, cache)
scheme.mode = smart_opts["mode"]
scheme.variant = smart_opts["variant"]

# Update colours
scheme.update_colours()
apply_colours(scheme.colours, scheme.mode)

# Run custom post-hook if configured
try:
cfg = json.loads(user_config_path.read_text()).get("wallpaper", {})
if post_hook := cfg.get("postHook"):
subprocess.run(
post_hook,
shell=True,
env={**os.environ, "WALLPAPER_PATH": str(wall)},
stderr=subprocess.DEVNULL,
)
except (FileNotFoundError, json.JSONDecodeError):
pass


def set_random(args: Namespace) -> None:
Expand Down