kitowall 3.5.14 → 3.5.27
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/README.md +26 -37
- package/dist/core/init.js +1 -49
- package/dist/core/live.js +221 -59
- package/dist/core/systemd.js +1 -5
- package/dist/managers/swww.js +1 -4
- package/dist/utils/exec.js +2 -23
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
`Kitowall` is a wallpaper manager for Hyprland/Wayland using `swww`.
|
|
6
6
|
|
|
7
|
-
Current version: `
|
|
7
|
+
Current version: `3.5.15`.
|
|
8
8
|
|
|
9
9
|
## What You Can Do
|
|
10
10
|
- Rotate wallpapers with transitions.
|
|
@@ -43,11 +43,35 @@ If your system needs it on Wayland:
|
|
|
43
43
|
WEBKIT_DISABLE_DMABUF_RENDERER=1 npm run tauri:dev
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
+
## AppImage (Recommended)
|
|
47
|
+
AppImage is now the primary desktop distribution for `kitowall-ui`.
|
|
48
|
+
|
|
49
|
+
1) Download the latest `Kitowall-<version>-x86_64.AppImage` from GitHub Releases.
|
|
50
|
+
2) Make it executable:
|
|
51
|
+
```bash
|
|
52
|
+
chmod +x ./Kitowall-*.AppImage
|
|
53
|
+
```
|
|
54
|
+
3) First-time host bootstrap (installs CLI/runtime dependencies on host):
|
|
55
|
+
```bash
|
|
56
|
+
./scripts/bootstrap-host.sh
|
|
57
|
+
```
|
|
58
|
+
If `bootstrap-host.sh` fails on Arch with errors like `zstd: undefined symbol: POOL_free` or `bsdtar: Write error`, repair core compression/archive libs and retry:
|
|
59
|
+
```bash
|
|
60
|
+
sudo pacman -Syu
|
|
61
|
+
sudo pacman -S --overwrite '*' zstd libarchive
|
|
62
|
+
```
|
|
63
|
+
Reason: this fixes host library mismatches (partial/out-of-sync updates) that break AUR package builds and package compression.
|
|
64
|
+
|
|
65
|
+
4) Initialize services:
|
|
66
|
+
```bash
|
|
67
|
+
kitowall init --namespace kitowall --apply --force
|
|
68
|
+
```
|
|
69
|
+
|
|
46
70
|
## Package / Release
|
|
47
71
|
- Release checklist: `RELEASE_CHECKLIST.md`
|
|
48
72
|
- Release notes: `RELEASE_NOTES_1.0.0.md`
|
|
49
73
|
- Dependencies: `DEPENDENCIES.md`
|
|
50
|
-
-
|
|
74
|
+
- AppImage CI: `.github/workflows/build-appimage.yml`
|
|
51
75
|
|
|
52
76
|
Main commands:
|
|
53
77
|
```bash
|
|
@@ -64,46 +88,11 @@ npm run package:ui
|
|
|
64
88
|
npm run package:all
|
|
65
89
|
```
|
|
66
90
|
|
|
67
|
-
## Flatpak (Linux)
|
|
68
|
-
```bash
|
|
69
|
-
# 0) Install host deps (Arch Linux)
|
|
70
|
-
./BOOTSTRAP_FLATPAK_BUILD_DEPS.sh
|
|
71
|
-
|
|
72
|
-
# 1) Build desktop binary
|
|
73
|
-
cd ui
|
|
74
|
-
npm run tauri:build
|
|
75
|
-
cd ..
|
|
76
|
-
|
|
77
|
-
# 2) Prepare flatpak sources (binary + icon)
|
|
78
|
-
./flatpak/prepare.sh
|
|
79
|
-
|
|
80
|
-
# 3) Build and install flatpak
|
|
81
|
-
flatpak-builder flatpak/build-dir flatpak/io.kitotsu.KitoWall.yml --user --install --force-clean
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
For Flathub source pipeline:
|
|
85
|
-
```bash
|
|
86
|
-
./BOOTSTRAP_FLATPAK_BUILD_DEPS.sh
|
|
87
|
-
./GENERATE_FLATHUB_SOURCES.sh 2.1.0 && ./BUILD_FLATPAK_FROMSOURCE.sh
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
Integrated local Flatpak (Kitowall + Kitsune + Kitsune-RenderCore in one app):
|
|
91
|
-
```bash
|
|
92
|
-
./BOOTSTRAP_FLATPAK_BUILD_DEPS.sh
|
|
93
|
-
./GENERATE_FLATHUB_SOURCES.sh 2.1.0
|
|
94
|
-
./BUILD_FLATPAK_INTEGRATED_LOCAL.sh
|
|
95
|
-
flatpak run io.kitotsu.KitoWall
|
|
96
|
-
```
|
|
97
|
-
|
|
98
91
|
## User Docs
|
|
99
92
|
- Current status: `STATUS.md`
|
|
100
93
|
- Config examples: `CONFIG_EXAMPLES.md`
|
|
101
94
|
- UI details: `ui/README.md`
|
|
102
95
|
|
|
103
|
-
## Known Issues
|
|
104
|
-
- Flatpak watch unit failing with `/app/bin/node` in user systemd:
|
|
105
|
-
- `issues/flatpak-watch-service-failed.md`
|
|
106
|
-
|
|
107
96
|
## Legal
|
|
108
97
|
- License: `LICENSE.md`
|
|
109
98
|
- Attribution notice: `NOTICE.md`
|
package/dist/core/init.js
CHANGED
|
@@ -17,9 +17,6 @@ function esc(a) {
|
|
|
17
17
|
return JSON.stringify(a);
|
|
18
18
|
}
|
|
19
19
|
async function runHostShell(cmd) {
|
|
20
|
-
if (process.env.FLATPAK_ID) {
|
|
21
|
-
return (0, exec_1.run)('flatpak-spawn', ['--host', 'sh', '-lc', cmd]);
|
|
22
|
-
}
|
|
23
20
|
return (0, exec_1.run)('sh', ['-lc', cmd]);
|
|
24
21
|
}
|
|
25
22
|
async function hostCmdExists(cmd) {
|
|
@@ -63,44 +60,6 @@ async function ensureHostDeps() {
|
|
|
63
60
|
`Install on host (Arch): sudo pacman -S --needed swww hyprland`);
|
|
64
61
|
}
|
|
65
62
|
}
|
|
66
|
-
async function ensureHostFlatpakBridges(appId) {
|
|
67
|
-
const home = (0, node_os_1.homedir)();
|
|
68
|
-
const localBinDir = (0, node_path_1.join)(home, '.local', 'bin');
|
|
69
|
-
ensureDir(localBinDir);
|
|
70
|
-
const writeBridge = (name, command) => {
|
|
71
|
-
const bridgePath = (0, node_path_1.join)(localBinDir, name);
|
|
72
|
-
const bridgeScript = `#!/usr/bin/env bash
|
|
73
|
-
set -euo pipefail
|
|
74
|
-
exec /usr/bin/flatpak run --command=${command} ${appId} "$@"
|
|
75
|
-
`;
|
|
76
|
-
(0, node_fs_1.writeFileSync)(bridgePath, bridgeScript, { encoding: 'utf8', mode: 0o755 });
|
|
77
|
-
return bridgePath;
|
|
78
|
-
};
|
|
79
|
-
writeBridge('kitowall', 'kitowall');
|
|
80
|
-
writeBridge('kitsune', 'kitsune');
|
|
81
|
-
const rendercoreBridgePath = writeBridge('kitsune-rendercore', 'kitsune-rendercore');
|
|
82
|
-
const userDir = (0, node_path_1.join)(home, '.config', 'systemd', 'user');
|
|
83
|
-
ensureDir(userDir);
|
|
84
|
-
const unitPath = (0, node_path_1.join)(userDir, 'kitsune-rendercore.service');
|
|
85
|
-
const unit = `
|
|
86
|
-
[Unit]
|
|
87
|
-
Description=Kitsune RenderCore Live Wallpaper (Flatpak bridge)
|
|
88
|
-
After=graphical-session.target
|
|
89
|
-
PartOf=graphical-session.target
|
|
90
|
-
|
|
91
|
-
[Service]
|
|
92
|
-
Type=simple
|
|
93
|
-
ExecStart=${rendercoreBridgePath}
|
|
94
|
-
Restart=on-failure
|
|
95
|
-
RestartSec=1
|
|
96
|
-
|
|
97
|
-
[Install]
|
|
98
|
-
WantedBy=default.target
|
|
99
|
-
`.trimStart();
|
|
100
|
-
(0, node_fs_1.writeFileSync)(unitPath, unit, 'utf8');
|
|
101
|
-
await (0, exec_1.run)('systemctl', ['--user', 'daemon-reload']);
|
|
102
|
-
await (0, exec_1.run)('systemctl', ['--user', 'enable', '--now', 'kitsune-rendercore.service']).catch(() => { });
|
|
103
|
-
}
|
|
104
63
|
async function disableIfExists(unit) {
|
|
105
64
|
await (0, exec_1.run)('systemctl', ['--user', 'disable', '--now', unit]).catch(() => { });
|
|
106
65
|
await (0, exec_1.run)('systemctl', ['--user', 'reset-failed', unit]).catch(() => { });
|
|
@@ -128,22 +87,15 @@ async function initKitowall(opts) {
|
|
|
128
87
|
const state = (0, state_1.loadState)(); // crea/migra state si hace falta
|
|
129
88
|
const ns = (opts.namespace && opts.namespace.trim()) ? opts.namespace.trim() : 'kitowall';
|
|
130
89
|
const force = !!opts.force;
|
|
131
|
-
const isFlatpak = Boolean(process.env.FLATPAK_ID);
|
|
132
|
-
const flatpakAppId = (process.env.FLATPAK_ID || 'io.kitotsu.KitoWall').trim();
|
|
133
90
|
// Validaciones mínimas
|
|
134
91
|
await ensureHostDeps();
|
|
135
|
-
if (isFlatpak) {
|
|
136
|
-
await ensureHostFlatpakBridges(flatpakAppId);
|
|
137
|
-
}
|
|
138
92
|
// Apagar servicios que pisan el wallpaper
|
|
139
93
|
await detectAndHandleConflicts(force);
|
|
140
94
|
const userDir = (0, node_path_1.join)((0, node_os_1.homedir)(), '.config', 'systemd', 'user');
|
|
141
95
|
ensureDir(userDir);
|
|
142
96
|
const nodePath = process.execPath;
|
|
143
97
|
const cliPath = (0, node_path_1.resolve)(process.argv[1]); // dist/cli.js absoluto
|
|
144
|
-
const cliInvoke =
|
|
145
|
-
? `/usr/bin/flatpak run --command=kitowall ${flatpakAppId}`
|
|
146
|
-
: `${JSON.stringify(nodePath)} ${JSON.stringify(cliPath)}`;
|
|
98
|
+
const cliInvoke = `${JSON.stringify(nodePath)} ${JSON.stringify(cliPath)}`;
|
|
147
99
|
const xdgRuntimeDir = (process.env.XDG_RUNTIME_DIR && process.env.XDG_RUNTIME_DIR.trim())
|
|
148
100
|
? process.env.XDG_RUNTIME_DIR.trim()
|
|
149
101
|
: `/run/user/${process.getuid?.() ?? 1000}`;
|
package/dist/core/live.js
CHANGED
|
@@ -47,8 +47,8 @@ const LIVE_BROWSER_UA = process.env.KITOWALL_LIVE_UA ||
|
|
|
47
47
|
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36';
|
|
48
48
|
const LIVE_BROWSER_UA_FALLBACK = process.env.KITOWALL_LIVE_UA_FALLBACK ||
|
|
49
49
|
'insomnia/12.3.1';
|
|
50
|
-
const INTEGRATED_RENDERCORE_ENV = '
|
|
51
|
-
const INTEGRATED_RENDERCORE_BIN_FALLBACK = '
|
|
50
|
+
const INTEGRATED_RENDERCORE_ENV = 'KITOWALL_INTEGRATED_RENDERCORE';
|
|
51
|
+
const INTEGRATED_RENDERCORE_BIN_FALLBACK = 'kitsune-rendercore';
|
|
52
52
|
function nowUnix() {
|
|
53
53
|
return Math.floor(Date.now() / 1000);
|
|
54
54
|
}
|
|
@@ -871,8 +871,6 @@ function rendercoreEnvPath() {
|
|
|
871
871
|
return node_path_1.default.join(node_os_1.default.homedir(), '.config', 'kitsune-rendercore', 'env');
|
|
872
872
|
}
|
|
873
873
|
function integratedRendercoreMode() {
|
|
874
|
-
if (!process.env.FLATPAK_ID)
|
|
875
|
-
return false;
|
|
876
874
|
const v = clean(process.env[INTEGRATED_RENDERCORE_ENV]).toLowerCase();
|
|
877
875
|
return v === '1' || v === 'true' || v === 'yes' || v === 'on';
|
|
878
876
|
}
|
|
@@ -1044,6 +1042,158 @@ function syncRendercoreEnv(defaults) {
|
|
|
1044
1042
|
}
|
|
1045
1043
|
node_fs_1.default.writeFileSync(p, `${lines.join('\n')}\n`, 'utf8');
|
|
1046
1044
|
}
|
|
1045
|
+
function shSingleQuote(v) {
|
|
1046
|
+
return `'${String(v).replace(/'/g, `'\\''`)}'`;
|
|
1047
|
+
}
|
|
1048
|
+
async function hostCommandExists(cmd) {
|
|
1049
|
+
try {
|
|
1050
|
+
await (0, exec_1.run)('sh', ['-lc', `command -v ${cmd} >/dev/null 2>&1`], { timeoutMs: 5000 });
|
|
1051
|
+
return true;
|
|
1052
|
+
}
|
|
1053
|
+
catch {
|
|
1054
|
+
return false;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
async function resolveHostExecutablePath(bin) {
|
|
1058
|
+
const raw = clean(bin);
|
|
1059
|
+
if (!raw)
|
|
1060
|
+
throw new Error('empty binary name');
|
|
1061
|
+
if (raw.includes('/')) {
|
|
1062
|
+
if (!node_fs_1.default.existsSync(raw))
|
|
1063
|
+
throw new Error(`binary not found: ${raw}`);
|
|
1064
|
+
return raw;
|
|
1065
|
+
}
|
|
1066
|
+
const out = await (0, exec_1.run)('sh', ['-lc', `command -v ${raw}`], { timeoutMs: 5000 });
|
|
1067
|
+
const resolved = clean(out.stdout.split('\n')[0]);
|
|
1068
|
+
if (!resolved)
|
|
1069
|
+
throw new Error(`binary not found in PATH: ${raw}`);
|
|
1070
|
+
return resolved;
|
|
1071
|
+
}
|
|
1072
|
+
async function installHostPackagesArch(packages) {
|
|
1073
|
+
if (packages.length === 0)
|
|
1074
|
+
return;
|
|
1075
|
+
const quoted = packages.map(shSingleQuote).join(' ');
|
|
1076
|
+
const cmd = `if command -v sudo >/dev/null 2>&1; then ` +
|
|
1077
|
+
`(sudo -n pacman -S --needed --noconfirm ${quoted} || sudo pacman -S --needed ${quoted}); ` +
|
|
1078
|
+
`else pacman -S --needed ${quoted}; fi`;
|
|
1079
|
+
await (0, exec_1.run)('sh', ['-lc', cmd], { timeoutMs: 240000 });
|
|
1080
|
+
}
|
|
1081
|
+
async function ensureRendercoreHostRuntimeDeps() {
|
|
1082
|
+
const missing = [];
|
|
1083
|
+
if (!(await hostCommandExists('ffmpeg')))
|
|
1084
|
+
missing.push('ffmpeg');
|
|
1085
|
+
if (!(await hostCommandExists('hyprctl')))
|
|
1086
|
+
missing.push('hyprctl');
|
|
1087
|
+
if (!(await hostCommandExists('systemctl')))
|
|
1088
|
+
missing.push('systemctl');
|
|
1089
|
+
if (missing.length === 0)
|
|
1090
|
+
return { installed: [], missing: [] };
|
|
1091
|
+
const canPacman = await hostCommandExists('pacman');
|
|
1092
|
+
const toInstall = new Set();
|
|
1093
|
+
if (missing.includes('ffmpeg'))
|
|
1094
|
+
toInstall.add('ffmpeg');
|
|
1095
|
+
if (missing.includes('hyprctl'))
|
|
1096
|
+
toInstall.add('hyprland');
|
|
1097
|
+
if (canPacman && toInstall.size > 0) {
|
|
1098
|
+
await installHostPackagesArch(Array.from(toInstall));
|
|
1099
|
+
}
|
|
1100
|
+
const afterMissing = [];
|
|
1101
|
+
if (!(await hostCommandExists('ffmpeg')))
|
|
1102
|
+
afterMissing.push('ffmpeg');
|
|
1103
|
+
if (!(await hostCommandExists('hyprctl')))
|
|
1104
|
+
afterMissing.push('hyprctl');
|
|
1105
|
+
if (!(await hostCommandExists('systemctl')))
|
|
1106
|
+
afterMissing.push('systemctl');
|
|
1107
|
+
return { installed: Array.from(toInstall), missing: afterMissing };
|
|
1108
|
+
}
|
|
1109
|
+
function rendercoreVideoMapPath() {
|
|
1110
|
+
return node_path_1.default.join(node_os_1.default.homedir(), '.config', 'kitsune-rendercore', 'video-map.conf');
|
|
1111
|
+
}
|
|
1112
|
+
function rendercoreUserServicePath() {
|
|
1113
|
+
return node_path_1.default.join(node_os_1.default.homedir(), '.config', 'systemd', 'user', 'kitsune-rendercore.service');
|
|
1114
|
+
}
|
|
1115
|
+
function readEnvPairs(p) {
|
|
1116
|
+
const out = {};
|
|
1117
|
+
if (!node_fs_1.default.existsSync(p))
|
|
1118
|
+
return out;
|
|
1119
|
+
const raw = node_fs_1.default.readFileSync(p, 'utf8');
|
|
1120
|
+
for (const line of raw.split('\n')) {
|
|
1121
|
+
const t = line.trim();
|
|
1122
|
+
if (!t || t.startsWith('#'))
|
|
1123
|
+
continue;
|
|
1124
|
+
const eq = t.indexOf('=');
|
|
1125
|
+
if (eq <= 0)
|
|
1126
|
+
continue;
|
|
1127
|
+
out[t.slice(0, eq).trim()] = t.slice(eq + 1).trim();
|
|
1128
|
+
}
|
|
1129
|
+
return out;
|
|
1130
|
+
}
|
|
1131
|
+
function writeEnvPairs(p, pairs) {
|
|
1132
|
+
const keys = Object.keys(pairs).sort((a, b) => a.localeCompare(b));
|
|
1133
|
+
const lines = keys.map((k) => `${k}=${pairs[k]}`);
|
|
1134
|
+
node_fs_1.default.writeFileSync(p, `${lines.join('\n')}\n`, 'utf8');
|
|
1135
|
+
}
|
|
1136
|
+
function ensureRendercoreServiceFiles(bin, defaults) {
|
|
1137
|
+
const envPath = rendercoreEnvPath();
|
|
1138
|
+
const mapPath = rendercoreVideoMapPath();
|
|
1139
|
+
const servicePath = rendercoreUserServicePath();
|
|
1140
|
+
(0, fs_1.ensureDir)(node_path_1.default.dirname(envPath));
|
|
1141
|
+
(0, fs_1.ensureDir)(node_path_1.default.dirname(servicePath));
|
|
1142
|
+
if (!node_fs_1.default.existsSync(mapPath))
|
|
1143
|
+
node_fs_1.default.writeFileSync(mapPath, '', 'utf8');
|
|
1144
|
+
const envPairs = readEnvPairs(envPath);
|
|
1145
|
+
envPairs.KRC_VIDEO_MAP_FILE = mapPath;
|
|
1146
|
+
envPairs.KRC_VIDEO_FPS = String(defaults.video_fps);
|
|
1147
|
+
envPairs.KRC_VIDEO_SPEED = String(defaults.video_speed);
|
|
1148
|
+
envPairs.KRC_HWACCEL = defaults.hwaccel;
|
|
1149
|
+
envPairs.KRC_QUALITY = defaults.quality;
|
|
1150
|
+
envPairs.KRC_PAUSE_ON_STEAM_GAME = defaults.pause_on_steam_game ? 'true' : 'false';
|
|
1151
|
+
envPairs.KRC_STEAM_POLL_MS = String(defaults.steam_poll_ms);
|
|
1152
|
+
writeEnvPairs(envPath, envPairs);
|
|
1153
|
+
const home = node_os_1.default.homedir();
|
|
1154
|
+
const pathEnv = `${home}/.local/bin:${home}/.cargo/bin:/usr/local/bin:/usr/bin:/bin`;
|
|
1155
|
+
const serviceBody = [
|
|
1156
|
+
'[Unit]',
|
|
1157
|
+
'Description=Kitsune RenderCore Live Wallpaper',
|
|
1158
|
+
'After=graphical-session.target',
|
|
1159
|
+
'PartOf=graphical-session.target',
|
|
1160
|
+
'',
|
|
1161
|
+
'[Service]',
|
|
1162
|
+
'Type=simple',
|
|
1163
|
+
`Environment=PATH=${pathEnv}`,
|
|
1164
|
+
`EnvironmentFile=-${envPath}`,
|
|
1165
|
+
`ExecStart=${bin}`,
|
|
1166
|
+
'Restart=on-failure',
|
|
1167
|
+
'RestartSec=1',
|
|
1168
|
+
'',
|
|
1169
|
+
'[Install]',
|
|
1170
|
+
'WantedBy=graphical-session.target',
|
|
1171
|
+
''
|
|
1172
|
+
].join('\n');
|
|
1173
|
+
node_fs_1.default.writeFileSync(servicePath, serviceBody, 'utf8');
|
|
1174
|
+
return { service_path: servicePath, env_path: envPath, map_path: mapPath };
|
|
1175
|
+
}
|
|
1176
|
+
function extractExecResult(err) {
|
|
1177
|
+
const result = err?.result;
|
|
1178
|
+
if (!result)
|
|
1179
|
+
return null;
|
|
1180
|
+
return {
|
|
1181
|
+
stdout: String(result.stdout ?? ''),
|
|
1182
|
+
stderr: String(result.stderr ?? ''),
|
|
1183
|
+
code: Number(result.code ?? 1)
|
|
1184
|
+
};
|
|
1185
|
+
}
|
|
1186
|
+
async function runSystemctlUser(args, acceptNonZero = false) {
|
|
1187
|
+
try {
|
|
1188
|
+
return await (0, exec_1.run)('systemctl', ['--user', ...args], { timeoutMs: 30000 });
|
|
1189
|
+
}
|
|
1190
|
+
catch (err) {
|
|
1191
|
+
const partial = extractExecResult(err);
|
|
1192
|
+
if (acceptNonZero && partial)
|
|
1193
|
+
return partial;
|
|
1194
|
+
throw err;
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1047
1197
|
async function resolvePost(provider, pageUrl) {
|
|
1048
1198
|
const html = await fetchHtml(pageUrl, pageUrl);
|
|
1049
1199
|
const parsed = liveParsePostFromHtml(provider, pageUrl, html);
|
|
@@ -1657,8 +1807,16 @@ async function liveApply(opts) {
|
|
|
1657
1807
|
integratedRendercoreStart(bin);
|
|
1658
1808
|
}
|
|
1659
1809
|
else {
|
|
1660
|
-
|
|
1661
|
-
|
|
1810
|
+
const deps = await ensureRendercoreHostRuntimeDeps();
|
|
1811
|
+
if (deps.missing.length > 0) {
|
|
1812
|
+
throw new Error(`Missing host dependencies: ${deps.missing.join(', ')}. ` +
|
|
1813
|
+
'Install on host (Arch): sudo pacman -S --needed ffmpeg hyprland');
|
|
1814
|
+
}
|
|
1815
|
+
const binPath = await resolveHostExecutablePath(bin);
|
|
1816
|
+
ensureRendercoreServiceFiles(binPath, index.apply_defaults);
|
|
1817
|
+
await runSystemctlUser(['daemon-reload']);
|
|
1818
|
+
await runSystemctlUser(['enable', 'kitsune-rendercore.service']);
|
|
1819
|
+
await runSystemctlUser(['start', 'kitsune-rendercore.service']);
|
|
1662
1820
|
}
|
|
1663
1821
|
withLiveLock(() => {
|
|
1664
1822
|
const current = readIndex();
|
|
@@ -1818,10 +1976,15 @@ async function liveDoctor(opts) {
|
|
|
1818
1976
|
if (!deps.runner_bin) {
|
|
1819
1977
|
fix.push(`Install ${runnerBin} and ensure it is available in PATH`);
|
|
1820
1978
|
}
|
|
1821
|
-
if (opts?.fix
|
|
1979
|
+
if (opts?.fix) {
|
|
1822
1980
|
try {
|
|
1823
|
-
|
|
1824
|
-
|
|
1981
|
+
const ensured = await ensureRendercoreHostRuntimeDeps();
|
|
1982
|
+
if (ensured.installed.length > 0) {
|
|
1983
|
+
fix.push(`Installed host packages: ${ensured.installed.join(', ')}`);
|
|
1984
|
+
}
|
|
1985
|
+
if (ensured.missing.length > 0) {
|
|
1986
|
+
fix.push(`Still missing host dependencies: ${ensured.missing.join(', ')}`);
|
|
1987
|
+
}
|
|
1825
1988
|
try {
|
|
1826
1989
|
await (0, exec_1.run)('ffmpeg', ['-version'], { timeoutMs: 4000 });
|
|
1827
1990
|
deps.ffmpeg = true;
|
|
@@ -1838,11 +2001,11 @@ async function liveDoctor(opts) {
|
|
|
1838
2001
|
}
|
|
1839
2002
|
}
|
|
1840
2003
|
catch (e) {
|
|
1841
|
-
fix.push(`Failed to
|
|
2004
|
+
fix.push(`Failed to install host dependencies: ${String(e)}`);
|
|
1842
2005
|
}
|
|
1843
2006
|
}
|
|
1844
2007
|
else if (!deps.ffmpeg || !deps.hyprctl) {
|
|
1845
|
-
fix.push(
|
|
2008
|
+
fix.push('Run `kitowall live doctor --fix` to install missing runtime dependencies');
|
|
1846
2009
|
}
|
|
1847
2010
|
return {
|
|
1848
2011
|
ok: Object.values(deps).every(Boolean) || (deps.hyprctl === false && Object.keys(deps).filter(k => k !== 'hyprctl').every(k => deps[k])),
|
|
@@ -1880,62 +2043,61 @@ async function liveServiceAutostart(action) {
|
|
|
1880
2043
|
};
|
|
1881
2044
|
}
|
|
1882
2045
|
let out;
|
|
1883
|
-
|
|
1884
|
-
const
|
|
1885
|
-
if (
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
stdout:
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
if (action === 'status' && partial) {
|
|
1899
|
-
out = partial;
|
|
1900
|
-
return {
|
|
1901
|
-
ok: true,
|
|
1902
|
-
action,
|
|
1903
|
-
runner: bin,
|
|
1904
|
-
stdout: out.stdout.trim(),
|
|
1905
|
-
stderr: out.stderr.trim(),
|
|
1906
|
-
code: out.code
|
|
2046
|
+
if (action === 'install') {
|
|
2047
|
+
const deps = await ensureRendercoreHostRuntimeDeps();
|
|
2048
|
+
if (deps.missing.length > 0) {
|
|
2049
|
+
const message = `Missing host dependencies: ${deps.missing.join(', ')}. ` +
|
|
2050
|
+
'Install on host (Arch): sudo pacman -S --needed ffmpeg hyprland';
|
|
2051
|
+
out = { stdout: '', stderr: message, code: 1 };
|
|
2052
|
+
}
|
|
2053
|
+
else {
|
|
2054
|
+
const binPath = await resolveHostExecutablePath(bin);
|
|
2055
|
+
const installed = ensureRendercoreServiceFiles(binPath, index.apply_defaults);
|
|
2056
|
+
await runSystemctlUser(['daemon-reload']);
|
|
2057
|
+
out = {
|
|
2058
|
+
stdout: `Installed user service at ${installed.service_path}`,
|
|
2059
|
+
stderr: '',
|
|
2060
|
+
code: 0
|
|
1907
2061
|
};
|
|
1908
2062
|
}
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
out = legacyPartial;
|
|
1919
|
-
return {
|
|
1920
|
-
ok: true,
|
|
1921
|
-
action,
|
|
1922
|
-
runner: bin,
|
|
1923
|
-
stdout: out.stdout.trim(),
|
|
1924
|
-
stderr: out.stderr.trim(),
|
|
1925
|
-
code: out.code
|
|
1926
|
-
};
|
|
1927
|
-
}
|
|
1928
|
-
throw legacyErr;
|
|
1929
|
-
}
|
|
2063
|
+
}
|
|
2064
|
+
else if (action === 'status') {
|
|
2065
|
+
out = await runSystemctlUser(['status', '--no-pager', 'kitsune-rendercore.service'], true);
|
|
2066
|
+
}
|
|
2067
|
+
else if (action === 'enable') {
|
|
2068
|
+
if (!node_fs_1.default.existsSync(rendercoreUserServicePath())) {
|
|
2069
|
+
const binPath = await resolveHostExecutablePath(bin);
|
|
2070
|
+
ensureRendercoreServiceFiles(binPath, index.apply_defaults);
|
|
2071
|
+
await runSystemctlUser(['daemon-reload']);
|
|
1930
2072
|
}
|
|
1931
|
-
|
|
1932
|
-
|
|
2073
|
+
out = await runSystemctlUser(['enable', 'kitsune-rendercore.service']);
|
|
2074
|
+
}
|
|
2075
|
+
else if (action === 'disable') {
|
|
2076
|
+
out = await runSystemctlUser(['disable', '--now', 'kitsune-rendercore.service'], true);
|
|
2077
|
+
}
|
|
2078
|
+
else if (action === 'start') {
|
|
2079
|
+
if (!node_fs_1.default.existsSync(rendercoreUserServicePath())) {
|
|
2080
|
+
const binPath = await resolveHostExecutablePath(bin);
|
|
2081
|
+
ensureRendercoreServiceFiles(binPath, index.apply_defaults);
|
|
2082
|
+
await runSystemctlUser(['daemon-reload']);
|
|
2083
|
+
}
|
|
2084
|
+
out = await runSystemctlUser(['start', 'kitsune-rendercore.service']);
|
|
2085
|
+
}
|
|
2086
|
+
else if (action === 'stop') {
|
|
2087
|
+
out = await runSystemctlUser(['stop', 'kitsune-rendercore.service'], true);
|
|
2088
|
+
}
|
|
2089
|
+
else {
|
|
2090
|
+
if (!node_fs_1.default.existsSync(rendercoreUserServicePath())) {
|
|
2091
|
+
const binPath = await resolveHostExecutablePath(bin);
|
|
2092
|
+
ensureRendercoreServiceFiles(binPath, index.apply_defaults);
|
|
2093
|
+
await runSystemctlUser(['daemon-reload']);
|
|
1933
2094
|
}
|
|
2095
|
+
out = await runSystemctlUser(['restart', 'kitsune-rendercore.service']);
|
|
1934
2096
|
}
|
|
1935
2097
|
return {
|
|
1936
2098
|
ok: true,
|
|
1937
2099
|
action,
|
|
1938
|
-
runner: bin
|
|
2100
|
+
runner: `${bin} (systemd-user)`,
|
|
1939
2101
|
stdout: out.stdout.trim(),
|
|
1940
2102
|
stderr: out.stderr.trim(),
|
|
1941
2103
|
code: out.code
|
package/dist/core/systemd.js
CHANGED
|
@@ -37,8 +37,6 @@ async function installSystemd(opts) {
|
|
|
37
37
|
ensureDir(userDir);
|
|
38
38
|
const every = systemdInterval(opts.every);
|
|
39
39
|
const ns = (opts.namespace && opts.namespace.trim()) ? opts.namespace.trim() : 'kitowall';
|
|
40
|
-
const isFlatpak = Boolean(process.env.FLATPAK_ID);
|
|
41
|
-
const flatpakAppId = (process.env.FLATPAK_ID || 'io.kitotsu.KitoWall').trim();
|
|
42
40
|
const nodePath = process.execPath;
|
|
43
41
|
const cliPath = (0, node_path_1.resolve)(process.argv[1] || '');
|
|
44
42
|
const xdgRuntimeDir = (process.env.XDG_RUNTIME_DIR && process.env.XDG_RUNTIME_DIR.trim())
|
|
@@ -53,9 +51,7 @@ async function installSystemd(opts) {
|
|
|
53
51
|
const waylandBootstrap = 'WAYLAND_DISPLAY="${WAYLAND_DISPLAY:-$(ls \\"$XDG_RUNTIME_DIR\\"/wayland-* 2>/dev/null | xargs -r -n1 basename | sort | tail -n1)}"; ' +
|
|
54
52
|
'if [ -z "$WAYLAND_DISPLAY" ]; then WAYLAND_DISPLAY=wayland-1; fi; ' +
|
|
55
53
|
'export WAYLAND_DISPLAY;';
|
|
56
|
-
const nextCmd =
|
|
57
|
-
? `${waylandBootstrap} exec /usr/bin/flatpak run --command=kitowall ${flatpakAppId} rotate-now --namespace ${ns} --force`
|
|
58
|
-
: `${waylandBootstrap} exec ${JSON.stringify(nodePath)} ${JSON.stringify(cliPath)} rotate-now --namespace ${JSON.stringify(ns)} --force`;
|
|
54
|
+
const nextCmd = `${waylandBootstrap} exec ${JSON.stringify(nodePath)} ${JSON.stringify(cliPath)} rotate-now --namespace ${JSON.stringify(ns)} --force`;
|
|
59
55
|
const kitowallNextService = `
|
|
60
56
|
[Unit]
|
|
61
57
|
Description=Kitowall apply next wallpapers
|
package/dist/managers/swww.js
CHANGED
|
@@ -8,10 +8,7 @@ function startSwwwDaemon(namespace) {
|
|
|
8
8
|
const args = [];
|
|
9
9
|
if (namespace)
|
|
10
10
|
args.push('--namespace', namespace);
|
|
11
|
-
const
|
|
12
|
-
const cmd = inFlatpak ? 'flatpak-spawn' : 'swww-daemon';
|
|
13
|
-
const spawnArgs = inFlatpak ? ['--host', 'swww-daemon', ...args] : args;
|
|
14
|
-
const child = (0, node_child_process_1.spawn)(cmd, spawnArgs, {
|
|
11
|
+
const child = (0, node_child_process_1.spawn)('swww-daemon', args, {
|
|
15
12
|
stdio: 'ignore',
|
|
16
13
|
detached: true
|
|
17
14
|
});
|
package/dist/utils/exec.js
CHANGED
|
@@ -5,29 +5,8 @@ exports.run = run;
|
|
|
5
5
|
const child_process_1 = require("child_process");
|
|
6
6
|
function run(cmd, args = [], options = {}) {
|
|
7
7
|
return new Promise((resolve, reject) => {
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
|| process.env.KITOWALL_FLATPAK_INTEGRATED_RENDERCORE === 'true');
|
|
11
|
-
const hostCommands = new Set([
|
|
12
|
-
'swww',
|
|
13
|
-
'swww-daemon',
|
|
14
|
-
'hyprctl',
|
|
15
|
-
'systemctl',
|
|
16
|
-
'xdg-open',
|
|
17
|
-
'steamcmd',
|
|
18
|
-
'ffmpeg',
|
|
19
|
-
'ffprobe',
|
|
20
|
-
'cargo',
|
|
21
|
-
'kitsune-livewallpaper',
|
|
22
|
-
'dd'
|
|
23
|
-
]);
|
|
24
|
-
if (!integratedRendercore) {
|
|
25
|
-
hostCommands.add('which');
|
|
26
|
-
hostCommands.add('kitsune-rendercore');
|
|
27
|
-
}
|
|
28
|
-
const useHost = isFlatpak && hostCommands.has(cmd);
|
|
29
|
-
const finalCmd = useHost ? 'flatpak-spawn' : cmd;
|
|
30
|
-
const finalArgs = useHost ? ['--host', cmd, ...args] : args;
|
|
8
|
+
const finalCmd = cmd;
|
|
9
|
+
const finalArgs = args;
|
|
31
10
|
const child = (0, child_process_1.spawn)(finalCmd, finalArgs, {
|
|
32
11
|
cwd: options.cwd,
|
|
33
12
|
env: options.env,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kitowall",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.27",
|
|
4
4
|
"description": "CLI/daemon for Hyprland wallpapers using swww with pack-based rotation.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"release:check": "npm run build && npm run test:e2e",
|
|
27
27
|
"package:cli": "npm pack",
|
|
28
28
|
"package:ui": "npm --prefix ui run tauri:build",
|
|
29
|
+
"package:appimage": "npm --prefix ui run tauri:build",
|
|
29
30
|
"package:all": "npm run release:check && npm run package:cli && npm run package:ui",
|
|
30
31
|
"test:smoke": "bash ./tests/smoke.e2e.sh",
|
|
31
32
|
"test:regression": "bash ./tests/regression.e2e.sh",
|