kitowall 2.5.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -131,11 +131,11 @@ Commands:
131
131
  we library List downloaded workshop items
132
132
  we scan-steam Detect Steam workshop folders and list downloaded ids
133
133
  we sync-steam Sync local Steam Workshop 431960 items into Kitsune downloads
134
- we app-status Detect if Wallpaper Engine and scene engine are installed
134
+ we app-status Detect if Wallpaper Engine is installed in Steam
135
135
  we active Show current livewallpaper authority/lock state
136
- we apply <id> --monitor <name> [--backend auto|mpvpaper|linux-wallpaperengine]
137
- Apply live wallpaper on one monitor (auto resolves by wallpaper type)
138
- we apply --map DP-1:<id1>,HDMI-A-1:<id2> [--backend auto|mpvpaper|linux-wallpaperengine]
136
+ we apply <id> --monitor <name> [--backend auto|mpvpaper]
137
+ Apply video live wallpaper on one monitor
138
+ we apply --map DP-1:<id1>,HDMI-A-1:<id2> [--backend auto|mpvpaper]
139
139
  Apply wallpapers in batch by monitor map
140
140
  we stop [--monitor <name> | --all] Stop livewallpaper instances and restore previous services
141
141
  we coexist enter|exit|status Temporarily stop/restore wallpaper rotation services
@@ -440,7 +440,7 @@ async function main() {
440
440
  const id = cleanOpt(args[2] ?? null);
441
441
  const monitor = cleanOpt(getOptionValue(args, '--monitor'));
442
442
  if (!id || !monitor) {
443
- throw new Error('Usage: we apply <id> --monitor <name> [--backend auto|mpvpaper|linux-wallpaperengine] OR we apply --map DP-1:<id1>,HDMI-A-1:<id2>');
443
+ throw new Error('Usage: we apply <id> --monitor <name> [--backend auto|mpvpaper] OR we apply --map DP-1:<id1>,HDMI-A-1:<id2>');
444
444
  }
445
445
  const out = await (0, workshop_1.workshopApply)({ id, monitor, backend });
446
446
  console.log(JSON.stringify(out, null, 2));
@@ -7,7 +7,6 @@ exports.setWorkshopApiKey = setWorkshopApiKey;
7
7
  exports.workshopGetSteamRoots = workshopGetSteamRoots;
8
8
  exports.workshopSetSteamRoots = workshopSetSteamRoots;
9
9
  exports.workshopWallpaperEngineStatus = workshopWallpaperEngineStatus;
10
- exports.workshopSceneEngineStatus = workshopSceneEngineStatus;
11
10
  exports.workshopScanSteamDownloads = workshopScanSteamDownloads;
12
11
  exports.workshopSyncSteamDownloads = workshopSyncSteamDownloads;
13
12
  exports.workshopSearch = workshopSearch;
@@ -46,74 +45,6 @@ function clean(input) {
46
45
  const v = input.trim();
47
46
  return v.length > 0 ? v : undefined;
48
47
  }
49
- function isExecutable(filePath) {
50
- try {
51
- node_fs_1.default.accessSync(filePath, node_fs_1.default.constants.X_OK);
52
- return true;
53
- }
54
- catch {
55
- return false;
56
- }
57
- }
58
- function resolveOnPath(binary) {
59
- const bin = clean(binary);
60
- if (!bin)
61
- return undefined;
62
- if (bin.includes('/')) {
63
- const resolved = node_path_1.default.resolve(bin);
64
- return node_fs_1.default.existsSync(resolved) && isExecutable(resolved) ? resolved : undefined;
65
- }
66
- const pathEnv = clean(process.env.PATH) ?? '';
67
- const parts = pathEnv.split(':').filter(Boolean);
68
- for (const p of parts) {
69
- const candidate = node_path_1.default.join(p, bin);
70
- if (node_fs_1.default.existsSync(candidate) && isExecutable(candidate))
71
- return candidate;
72
- }
73
- return undefined;
74
- }
75
- function buildSceneEngineEnv(enginePath) {
76
- const env = { ...process.env };
77
- const libPaths = [];
78
- const seen = new Set();
79
- const pushLibPath = (candidate) => {
80
- const value = clean(candidate);
81
- if (!value || seen.has(value))
82
- return;
83
- seen.add(value);
84
- libPaths.push(value);
85
- };
86
- const current = clean(process.env.LD_LIBRARY_PATH);
87
- if (current) {
88
- for (const segment of current.split(':')) {
89
- const v = clean(segment);
90
- if (v)
91
- pushLibPath(v);
92
- }
93
- }
94
- const resolvedEngine = clean(enginePath);
95
- if (resolvedEngine) {
96
- const engineDir = node_path_1.default.dirname(resolvedEngine);
97
- if (node_fs_1.default.existsSync(node_path_1.default.join(engineDir, 'libcef.so'))) {
98
- pushLibPath(engineDir);
99
- }
100
- const siblingLib = node_path_1.default.join(engineDir, 'lib');
101
- if (node_fs_1.default.existsSync(siblingLib) && node_fs_1.default.statSync(siblingLib).isDirectory()) {
102
- pushLibPath(siblingLib);
103
- }
104
- }
105
- // Arch linux-wallpaperengine installs runtime libs here when bypassing the wrapper.
106
- if (node_fs_1.default.existsSync('/opt/linux-wallpaperengine/libcef.so')) {
107
- pushLibPath('/opt/linux-wallpaperengine');
108
- }
109
- if (node_fs_1.default.existsSync('/opt/linux-wallpaperengine/lib') && node_fs_1.default.statSync('/opt/linux-wallpaperengine/lib').isDirectory()) {
110
- pushLibPath('/opt/linux-wallpaperengine/lib');
111
- }
112
- if (libPaths.length > 0) {
113
- env.LD_LIBRARY_PATH = libPaths.join(':');
114
- }
115
- return env;
116
- }
117
48
  function getWePaths() {
118
49
  const root = node_path_1.default.join(node_os_1.default.homedir(), '.local', 'share', 'kitsune', 'we');
119
50
  return {
@@ -199,6 +130,7 @@ function getCoexistServices() {
199
130
  const cfg = readWeConfig();
200
131
  const defaults = [
201
132
  'swww-daemon.service',
133
+ 'swww-daemon@kitowall.service',
202
134
  'hyprwall-watch.service',
203
135
  'hyprwall-next.timer',
204
136
  'kitowall-next.timer'
@@ -601,41 +533,13 @@ function workshopWallpaperEngineStatus() {
601
533
  if (node_fs_1.default.existsSync(manifest))
602
534
  manifests.push(manifest);
603
535
  }
604
- const engine = workshopSceneEngineStatus();
605
536
  return {
606
537
  ok: true,
607
538
  installed: manifests.length > 0,
608
539
  manifests,
609
- steamapps,
610
- engine
540
+ steamapps
611
541
  };
612
542
  }
613
- function workshopSceneEngineStatus() {
614
- const configured = clean(process.env.KITOWALL_SCENE_ENGINE_CMD);
615
- const candidates = configured
616
- ? [configured]
617
- : ['linux-wallpaperengine', 'linux-wallpaperengine-cli', 'wallpaper-engine'];
618
- for (const cmd of candidates) {
619
- const resolved = resolveOnPath(cmd);
620
- if (!resolved)
621
- continue;
622
- const verOut = (0, node_child_process_1.spawnSync)(resolved, ['--version'], {
623
- encoding: 'utf8',
624
- timeout: 2500,
625
- env: buildSceneEngineEnv(resolved)
626
- });
627
- const stdout = clean(verOut.stdout) ?? '';
628
- const stderr = clean(verOut.stderr) ?? '';
629
- const version = (stdout.split('\n')[0] || stderr.split('\n')[0] || '').trim();
630
- return {
631
- installed: true,
632
- path: resolved,
633
- version: version || undefined,
634
- command: cmd
635
- };
636
- }
637
- return { installed: false, command: configured ?? 'linux-wallpaperengine' };
638
- }
639
543
  function findPreviewCandidate(dir) {
640
544
  if (!node_fs_1.default.existsSync(dir))
641
545
  return undefined;
@@ -1334,34 +1238,6 @@ function spawnMpvpaper(monitor, entry) {
1334
1238
  }, 180);
1335
1239
  });
1336
1240
  }
1337
- function spawnSceneEngine(monitor, wallpaperDir) {
1338
- const engine = workshopSceneEngineStatus();
1339
- if (!engine.installed || !engine.path) {
1340
- throw new Error('Scene engine not installed. Install linux-wallpaperengine first.');
1341
- }
1342
- const enginePath = engine.path;
1343
- return new Promise((resolve, reject) => {
1344
- const child = (0, node_child_process_1.spawn)(enginePath, ['--screen-root', monitor, '--bg', wallpaperDir], {
1345
- detached: true,
1346
- stdio: 'ignore',
1347
- env: buildSceneEngineEnv(enginePath)
1348
- });
1349
- let settled = false;
1350
- const done = (fn) => {
1351
- if (settled)
1352
- return;
1353
- settled = true;
1354
- fn();
1355
- };
1356
- child.once('error', (err) => done(() => reject(err)));
1357
- setTimeout(() => {
1358
- done(() => {
1359
- child.unref();
1360
- resolve(child.pid ?? 0);
1361
- });
1362
- }, 220);
1363
- });
1364
- }
1365
1241
  async function workshopApply(input) {
1366
1242
  const id = clean(input.id);
1367
1243
  const monitor = clean(input.monitor);
@@ -1381,8 +1257,8 @@ async function workshopApply(input) {
1381
1257
  const type = project.type !== 'unknown'
1382
1258
  ? project.type
1383
1259
  : detectTypeFromEntry(inferredEntry ?? node_path_1.default.join(dir, 'scene.json'));
1384
- if (type === 'web' || type === 'application' || type === 'unknown') {
1385
- throw new Error(`Unsupported wallpaper type for apply: ${type}. Supported: video, scene.`);
1260
+ if (type !== 'video') {
1261
+ throw new Error(`Unsupported wallpaper type for apply: ${type}. Supported: video (mpvpaper).`);
1386
1262
  }
1387
1263
  let state = readActiveState();
1388
1264
  if (!state) {
@@ -1400,30 +1276,16 @@ async function workshopApply(input) {
1400
1276
  }
1401
1277
  let backend;
1402
1278
  let pid = 0;
1403
- if (type === 'video') {
1404
- if (!(requestedBackend === 'auto' || requestedBackend === 'mpvpaper')) {
1405
- throw new Error(`Invalid backend for video wallpaper: ${requestedBackend}`);
1406
- }
1407
- if (!inferredEntry || !node_fs_1.default.existsSync(inferredEntry)) {
1408
- throw new Error(`Video entry not found for wallpaper: ${id}`);
1409
- }
1410
- backend = 'mpvpaper';
1411
- pid = await spawnMpvpaper(monitor, inferredEntry).catch((err) => {
1412
- throw new Error(`Failed to launch mpvpaper: ${err instanceof Error ? err.message : String(err)}`);
1413
- });
1279
+ if (!(requestedBackend === 'auto' || requestedBackend === 'mpvpaper')) {
1280
+ throw new Error(`Invalid backend for video wallpaper: ${requestedBackend}`);
1414
1281
  }
1415
- else if (type === 'scene') {
1416
- if (!(requestedBackend === 'auto' || requestedBackend === 'linux-wallpaperengine')) {
1417
- throw new Error(`Invalid backend for scene wallpaper: ${requestedBackend}`);
1418
- }
1419
- backend = 'linux-wallpaperengine';
1420
- pid = await spawnSceneEngine(monitor, dir).catch((err) => {
1421
- throw new Error(`Failed to launch scene engine: ${err instanceof Error ? err.message : String(err)}`);
1422
- });
1423
- }
1424
- else {
1425
- throw new Error(`Unsupported wallpaper type for apply: ${type}`);
1282
+ if (!inferredEntry || !node_fs_1.default.existsSync(inferredEntry)) {
1283
+ throw new Error(`Video entry not found for wallpaper: ${id}`);
1426
1284
  }
1285
+ backend = 'mpvpaper';
1286
+ pid = await spawnMpvpaper(monitor, inferredEntry).catch((err) => {
1287
+ throw new Error(`Failed to launch mpvpaper: ${err instanceof Error ? err.message : String(err)}`);
1288
+ });
1427
1289
  if (!pid) {
1428
1290
  throw new Error(`${backend} started without pid`);
1429
1291
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kitowall",
3
- "version": "2.5.0",
3
+ "version": "2.7.0",
4
4
  "description": "CLI/daemon for Hyprland wallpapers using swww with pack-based rotation.",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "type": "commonjs",