import { monitorDirectory } from "@/utils/system"; import Thumbnailer from "@/utils/thumbnailer"; import { execAsync, GObject, property, register } from "astal"; import { wallpapers as config } from "config"; export interface IWallpaper { path: string; thumbnail?: string; } export interface ICategory { path: string; wallpapers: IWallpaper[]; } @register({ GTypeName: "Wallpapers" }) export default class Wallpapers extends GObject.Object { static instance: Wallpapers; static get_default() { if (!this.instance) this.instance = new Wallpapers(); return this.instance; } #list: IWallpaper[] = []; #categories: ICategory[] = []; @property(Object) get list() { return this.#list; } @property(Object) get categories() { return this.#categories; } #listDir(path: { path: string; recursive: boolean }, type: "f" | "d") { const absPath = path.path.replace("~", HOME); const maxDepth = path.recursive ? "" : "-maxdepth 1"; return execAsync(`find ${absPath} ${maxDepth} -path '*/.*' -prune -o -type ${type} -print`); } async update() { const results = await Promise.allSettled( config.paths.get().map(async p => ({ path: p, files: await this.#listDir(p, "f") })) ); const successes = results.filter(r => r.status === "fulfilled").map(r => r.value); if (!successes.length) { this.#list = []; this.notify("list"); this.#categories = []; this.notify("categories"); return; } const files = successes.map(r => r.files.replaceAll("\n", " ")).join(" "); const list = (await execAsync(["fish", "-c", `identify -ping -format '%i\n' ${files} ; true`])).split("\n"); this.#list = await Promise.all(list.map(async p => ({ path: p, thumbnail: await Thumbnailer.thumbnail(p) }))); this.notify("list"); const categories = await Promise.all(successes.map(r => this.#listDir(r.path, "d"))); this.#categories = categories .flatMap(c => c.split("\n")) .map(c => ({ path: c, wallpapers: this.#list.filter(w => w.path.startsWith(c)) })) .filter(c => c.wallpapers.length > 0); this.notify("categories"); } constructor() { super(); this.update().catch(console.error); let monitors = config.paths .get() .map(p => monitorDirectory(p.path, () => this.update().catch(console.error), p.recursive)); config.paths.subscribe(v => { this.update().catch(console.error); for (const m of monitors) m.cancel(); monitors = v.map(p => monitorDirectory(p.path, () => this.update().catch(console.error), p.recursive)); }); } }