kitowall 3.1.0 → 3.3.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 +44 -61
- package/dist/core/live.js +197 -183
- package/dist/core/workshop.js +64 -3
- package/dist/utils/exec.js +1 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -55,6 +55,7 @@ const staticUrl_1 = require("./adapters/staticUrl");
|
|
|
55
55
|
const logs_1 = require("./core/logs");
|
|
56
56
|
const node_fs_1 = require("node:fs");
|
|
57
57
|
const node_path_1 = require("node:path");
|
|
58
|
+
const exec_1 = require("./utils/exec");
|
|
58
59
|
const workshop_1 = require("./core/workshop");
|
|
59
60
|
const live_1 = require("./core/live");
|
|
60
61
|
function getCliVersion() {
|
|
@@ -159,17 +160,11 @@ Commands:
|
|
|
159
160
|
live remove <id> [--delete-files]
|
|
160
161
|
live thumb regen [--id <id>] [--all]
|
|
161
162
|
live open [--id <id>] Print folder path (root or item folder)
|
|
163
|
+
live service-autostart <install|enable|disable|start|stop|restart|status>
|
|
162
164
|
live config show
|
|
163
|
-
live config wallpaper-show --id <id>
|
|
164
|
-
live config wallpaper --id <id> [--keep-services true|false] [--mute-audio true|false] [--profile performance|balanced|quality]
|
|
165
|
-
[--display-fps <n|off>] [--seamless-loop true|false] [--loop-crossfade true|false]
|
|
166
|
-
[--loop-crossfade-seconds <n>] [--optimize true|false]
|
|
167
|
-
[--proxy-width <n>] [--proxy-fps <n>] [--proxy-crf <n>]
|
|
168
165
|
live config runner [--bin-name <name>]
|
|
169
|
-
live config apply-defaults [--
|
|
170
|
-
[--
|
|
171
|
-
[--loop-crossfade-seconds <n>] [--profile performance|balanced|quality]
|
|
172
|
-
[--seamless-loop true|false] [--loop-crossfade true|false] [--optimize true|false]
|
|
166
|
+
live config apply-defaults [--video-fps <n>] [--video-speed <n>] [--hwaccel auto|nvdec|vaapi|none]
|
|
167
|
+
[--quality low|medium|high|ultra] [--pause-on-steam-game true|false] [--steam-poll-ms <n>]
|
|
173
168
|
live doctor [--fix]
|
|
174
169
|
check [--namespace <ns>] [--json] Quick system check (no changes)
|
|
175
170
|
|
|
@@ -266,6 +261,21 @@ function formatError(err) {
|
|
|
266
261
|
}
|
|
267
262
|
return { message, hint, code };
|
|
268
263
|
}
|
|
264
|
+
async function switchToStaticWallpaperMode() {
|
|
265
|
+
const liveBin = cleanOpt(process.env.KITOWALL_LIVE_RUNNER_BIN ?? null) ?? 'kitsune-rendercore';
|
|
266
|
+
try {
|
|
267
|
+
await (0, exec_1.run)(liveBin, ['service', 'disable'], { timeoutMs: 20000 });
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
270
|
+
// best effort
|
|
271
|
+
}
|
|
272
|
+
try {
|
|
273
|
+
await (0, workshop_1.workshopCoexistenceExit)();
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
// best effort
|
|
277
|
+
}
|
|
278
|
+
}
|
|
269
279
|
async function main() {
|
|
270
280
|
const args = process.argv.slice(2);
|
|
271
281
|
outputJsonOnError = args.includes('--json');
|
|
@@ -511,7 +521,7 @@ async function main() {
|
|
|
511
521
|
if (cmd === 'live') {
|
|
512
522
|
const action = cleanOpt(args[1] ?? null);
|
|
513
523
|
if (!action)
|
|
514
|
-
throw new Error('Usage: live <init|list|browse|search|resolve|preview|preview-clear|fetch|apply|auto-apply|favorite|remove|thumb|open|config|doctor> ...');
|
|
524
|
+
throw new Error('Usage: live <init|list|browse|search|resolve|preview|preview-clear|fetch|apply|auto-apply|favorite|remove|thumb|open|service-autostart|config|doctor> ...');
|
|
515
525
|
if (action === 'init') {
|
|
516
526
|
console.log(JSON.stringify((0, live_1.liveInit)(), null, 2));
|
|
517
527
|
return;
|
|
@@ -652,6 +662,21 @@ async function main() {
|
|
|
652
662
|
console.log(JSON.stringify(out, null, 2));
|
|
653
663
|
return;
|
|
654
664
|
}
|
|
665
|
+
if (action === 'service-autostart') {
|
|
666
|
+
const sub = cleanOpt(args[2] ?? null);
|
|
667
|
+
if (sub !== 'install' &&
|
|
668
|
+
sub !== 'enable' &&
|
|
669
|
+
sub !== 'disable' &&
|
|
670
|
+
sub !== 'status' &&
|
|
671
|
+
sub !== 'start' &&
|
|
672
|
+
sub !== 'stop' &&
|
|
673
|
+
sub !== 'restart') {
|
|
674
|
+
throw new Error('Usage: live service-autostart <install|enable|disable|start|stop|restart|status>');
|
|
675
|
+
}
|
|
676
|
+
const out = await (0, live_1.liveServiceAutostart)(sub);
|
|
677
|
+
console.log(JSON.stringify(out, null, 2));
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
655
680
|
if (action === 'doctor') {
|
|
656
681
|
const out = await (0, live_1.liveDoctor)({ fix: args.includes('--fix') });
|
|
657
682
|
console.log(JSON.stringify(out, null, 2));
|
|
@@ -663,39 +688,6 @@ async function main() {
|
|
|
663
688
|
console.log(JSON.stringify((0, live_1.liveGetConfig)(), null, 2));
|
|
664
689
|
return;
|
|
665
690
|
}
|
|
666
|
-
if (sub === 'wallpaper-show') {
|
|
667
|
-
const id = cleanOpt(getOptionValue(args, '--id'));
|
|
668
|
-
if (!id)
|
|
669
|
-
throw new Error('Usage: live config wallpaper-show --id <id>');
|
|
670
|
-
console.log(JSON.stringify((0, live_1.liveGetWallpaperConfig)(id), null, 2));
|
|
671
|
-
return;
|
|
672
|
-
}
|
|
673
|
-
if (sub === 'wallpaper') {
|
|
674
|
-
const id = cleanOpt(getOptionValue(args, '--id'));
|
|
675
|
-
if (!id) {
|
|
676
|
-
throw new Error('Usage: live config wallpaper --id <id> [--keep-services true|false] [--mute-audio true|false] [--profile performance|balanced|quality] [--display-fps <n|off>] [--seamless-loop true|false] [--loop-crossfade true|false] [--loop-crossfade-seconds <n>] [--optimize true|false] [--proxy-width <n>] [--proxy-fps <n>] [--proxy-crf <n>]');
|
|
677
|
-
}
|
|
678
|
-
const profile = cleanOpt(getOptionValue(args, '--profile'));
|
|
679
|
-
const displayFpsRaw = cleanOpt(getOptionValue(args, '--display-fps'));
|
|
680
|
-
const display_fps = !displayFpsRaw
|
|
681
|
-
? undefined
|
|
682
|
-
: (displayFpsRaw.toLowerCase() === 'off' ? null : Number(displayFpsRaw));
|
|
683
|
-
const out = (0, live_1.liveSetWallpaperConfig)(id, {
|
|
684
|
-
keep_services: parseBool(getOptionValue(args, '--keep-services')),
|
|
685
|
-
mute_audio: parseBool(getOptionValue(args, '--mute-audio')),
|
|
686
|
-
profile: (profile === 'performance' || profile === 'balanced' || profile === 'quality') ? profile : undefined,
|
|
687
|
-
display_fps: (display_fps === undefined || display_fps === null || Number.isFinite(display_fps)) ? display_fps : undefined,
|
|
688
|
-
seamless_loop: parseBool(getOptionValue(args, '--seamless-loop')),
|
|
689
|
-
loop_crossfade: parseBool(getOptionValue(args, '--loop-crossfade')),
|
|
690
|
-
loop_crossfade_seconds: cleanOpt(getOptionValue(args, '--loop-crossfade-seconds')) ? Number(cleanOpt(getOptionValue(args, '--loop-crossfade-seconds'))) : undefined,
|
|
691
|
-
optimize: parseBool(getOptionValue(args, '--optimize')),
|
|
692
|
-
proxy_width: cleanOpt(getOptionValue(args, '--proxy-width')) ? Number(cleanOpt(getOptionValue(args, '--proxy-width'))) : undefined,
|
|
693
|
-
proxy_fps: cleanOpt(getOptionValue(args, '--proxy-fps')) ? Number(cleanOpt(getOptionValue(args, '--proxy-fps'))) : undefined,
|
|
694
|
-
proxy_crf: cleanOpt(getOptionValue(args, '--proxy-crf')) ? Number(cleanOpt(getOptionValue(args, '--proxy-crf'))) : undefined
|
|
695
|
-
});
|
|
696
|
-
console.log(JSON.stringify(out, null, 2));
|
|
697
|
-
return;
|
|
698
|
-
}
|
|
699
691
|
if (sub === 'runner') {
|
|
700
692
|
const out = (0, live_1.liveSetRunner)({
|
|
701
693
|
bin_name: cleanOpt(getOptionValue(args, '--bin-name')) ?? undefined
|
|
@@ -704,24 +696,13 @@ async function main() {
|
|
|
704
696
|
return;
|
|
705
697
|
}
|
|
706
698
|
if (sub === 'apply-defaults') {
|
|
707
|
-
const displayFpsRaw = cleanOpt(getOptionValue(args, '--display-fps'));
|
|
708
|
-
const display_fps = !displayFpsRaw
|
|
709
|
-
? undefined
|
|
710
|
-
: (displayFpsRaw.toLowerCase() === 'off' ? null : Number(displayFpsRaw));
|
|
711
699
|
const out = (0, live_1.liveSetApplyDefaults)({
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
loop_crossfade_seconds: cleanOpt(getOptionValue(args, '--loop-crossfade-seconds')) ? Number(cleanOpt(getOptionValue(args, '--loop-crossfade-seconds'))) : undefined,
|
|
719
|
-
optimize: parseBool(getOptionValue(args, '--optimize')),
|
|
720
|
-
proxy_width_hd: cleanOpt(getOptionValue(args, '--proxy-width-hd')) ? Number(cleanOpt(getOptionValue(args, '--proxy-width-hd'))) : undefined,
|
|
721
|
-
proxy_width_4k: cleanOpt(getOptionValue(args, '--proxy-width-4k')) ? Number(cleanOpt(getOptionValue(args, '--proxy-width-4k'))) : undefined,
|
|
722
|
-
proxy_fps: cleanOpt(getOptionValue(args, '--proxy-fps')) ? Number(cleanOpt(getOptionValue(args, '--proxy-fps'))) : undefined,
|
|
723
|
-
proxy_crf_hd: cleanOpt(getOptionValue(args, '--proxy-crf-hd')) ? Number(cleanOpt(getOptionValue(args, '--proxy-crf-hd'))) : undefined,
|
|
724
|
-
proxy_crf_4k: cleanOpt(getOptionValue(args, '--proxy-crf-4k')) ? Number(cleanOpt(getOptionValue(args, '--proxy-crf-4k'))) : undefined
|
|
700
|
+
video_fps: cleanOpt(getOptionValue(args, '--video-fps')) ? Number(cleanOpt(getOptionValue(args, '--video-fps'))) : undefined,
|
|
701
|
+
video_speed: cleanOpt(getOptionValue(args, '--video-speed')) ? Number(cleanOpt(getOptionValue(args, '--video-speed'))) : undefined,
|
|
702
|
+
hwaccel: cleanOpt(getOptionValue(args, '--hwaccel')) ?? undefined,
|
|
703
|
+
quality: cleanOpt(getOptionValue(args, '--quality')) ?? undefined,
|
|
704
|
+
pause_on_steam_game: parseBool(getOptionValue(args, '--pause-on-steam-game')),
|
|
705
|
+
steam_poll_ms: cleanOpt(getOptionValue(args, '--steam-poll-ms')) ? Number(cleanOpt(getOptionValue(args, '--steam-poll-ms'))) : undefined
|
|
725
706
|
});
|
|
726
707
|
console.log(JSON.stringify(out, null, 2));
|
|
727
708
|
return;
|
|
@@ -732,7 +713,7 @@ async function main() {
|
|
|
732
713
|
console.log(JSON.stringify((0, live_1.liveViewData)(), null, 2));
|
|
733
714
|
return;
|
|
734
715
|
}
|
|
735
|
-
throw new Error('Usage: live <init|list|browse|search|resolve|preview|preview-clear|fetch|apply|auto-apply|favorite|remove|thumb|open|config|doctor> ...');
|
|
716
|
+
throw new Error('Usage: live <init|list|browse|search|resolve|preview|preview-clear|fetch|apply|auto-apply|favorite|remove|thumb|open|service-autostart|config|doctor> ...');
|
|
736
717
|
}
|
|
737
718
|
// Regular commands (need config/state)
|
|
738
719
|
const config = (0, config_1.loadConfig)();
|
|
@@ -1514,6 +1495,7 @@ async function main() {
|
|
|
1514
1495
|
state.mode = value;
|
|
1515
1496
|
state.last_updated = Date.now();
|
|
1516
1497
|
(0, state_1.saveState)(state);
|
|
1498
|
+
await switchToStaticWallpaperMode();
|
|
1517
1499
|
console.log(JSON.stringify({ ok: true, mode: state.mode }, null, 2));
|
|
1518
1500
|
return;
|
|
1519
1501
|
}
|
|
@@ -1531,6 +1513,7 @@ async function main() {
|
|
|
1531
1513
|
}, null, 2));
|
|
1532
1514
|
return;
|
|
1533
1515
|
}
|
|
1516
|
+
await switchToStaticWallpaperMode();
|
|
1534
1517
|
const result = await controller.applyNext(pack, namespace);
|
|
1535
1518
|
console.log(JSON.stringify({
|
|
1536
1519
|
pack: result.pack,
|
package/dist/core/live.js
CHANGED
|
@@ -22,6 +22,7 @@ exports.liveThumbRegen = liveThumbRegen;
|
|
|
22
22
|
exports.liveAutoApplySet = liveAutoApplySet;
|
|
23
23
|
exports.liveAutoApplyUnset = liveAutoApplyUnset;
|
|
24
24
|
exports.liveDoctor = liveDoctor;
|
|
25
|
+
exports.liveServiceAutostart = liveServiceAutostart;
|
|
25
26
|
exports.liveSetRunner = liveSetRunner;
|
|
26
27
|
exports.liveGetWallpaperConfig = liveGetWallpaperConfig;
|
|
27
28
|
exports.liveSetWallpaperConfig = liveSetWallpaperConfig;
|
|
@@ -37,6 +38,7 @@ const promises_1 = require("node:stream/promises");
|
|
|
37
38
|
const exec_1 = require("../utils/exec");
|
|
38
39
|
const net_1 = require("../utils/net");
|
|
39
40
|
const fs_1 = require("../utils/fs");
|
|
41
|
+
const workshop_1 = require("./workshop");
|
|
40
42
|
const LIVE_LOCK_STALE_MS = 10 * 60 * 1000;
|
|
41
43
|
const LIVE_DOWNLOAD_MIN_BYTES = 3 * 1024 * 1024;
|
|
42
44
|
const LIVE_PREVIEW_MIN_BYTES = 64 * 1024;
|
|
@@ -103,7 +105,7 @@ function defaultRunnerConfig() {
|
|
|
103
105
|
return {
|
|
104
106
|
mode: 'bin',
|
|
105
107
|
cargo_project_dir: process.env.KITOWALL_LIVE_RUNNER_PROJECT?.trim() || '',
|
|
106
|
-
bin_name: process.env.KITOWALL_LIVE_RUNNER_BIN?.trim() || 'kitsune-
|
|
108
|
+
bin_name: process.env.KITOWALL_LIVE_RUNNER_BIN?.trim() || 'kitsune-rendercore'
|
|
107
109
|
};
|
|
108
110
|
}
|
|
109
111
|
function defaultIndex() {
|
|
@@ -112,19 +114,12 @@ function defaultIndex() {
|
|
|
112
114
|
items: [],
|
|
113
115
|
per_monitor: {},
|
|
114
116
|
apply_defaults: {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
loop_crossfade_seconds: 0.35,
|
|
122
|
-
optimize: true,
|
|
123
|
-
proxy_width_hd: 1920,
|
|
124
|
-
proxy_width_4k: 3840,
|
|
125
|
-
proxy_fps: 60,
|
|
126
|
-
proxy_crf_hd: 18,
|
|
127
|
-
proxy_crf_4k: 16
|
|
117
|
+
video_fps: 30,
|
|
118
|
+
video_speed: 1.0,
|
|
119
|
+
hwaccel: 'auto',
|
|
120
|
+
quality: 'high',
|
|
121
|
+
pause_on_steam_game: true,
|
|
122
|
+
steam_poll_ms: 1000
|
|
128
123
|
},
|
|
129
124
|
runner: defaultRunnerConfig()
|
|
130
125
|
};
|
|
@@ -136,10 +131,7 @@ function ensureIndexShape(index) {
|
|
|
136
131
|
version: 1,
|
|
137
132
|
items: items.filter(v => v && typeof v === 'object'),
|
|
138
133
|
per_monitor: (index.per_monitor && typeof index.per_monitor === 'object') ? index.per_monitor : {},
|
|
139
|
-
apply_defaults:
|
|
140
|
-
...base.apply_defaults,
|
|
141
|
-
...(index.apply_defaults ?? {})
|
|
142
|
-
},
|
|
134
|
+
apply_defaults: normalizeApplyDefaults(index.apply_defaults, base.apply_defaults),
|
|
143
135
|
runner: {
|
|
144
136
|
...base.runner,
|
|
145
137
|
...(index.runner ?? {}),
|
|
@@ -209,7 +201,21 @@ function readIndex() {
|
|
|
209
201
|
}
|
|
210
202
|
try {
|
|
211
203
|
const raw = node_fs_1.default.readFileSync(idxPath, 'utf8');
|
|
212
|
-
|
|
204
|
+
const parsed = ensureIndexShape(JSON.parse(raw));
|
|
205
|
+
// Auto-migrate legacy runner to RenderCore.
|
|
206
|
+
if (clean(parsed.runner.bin_name) === 'kitsune-livewallpaper') {
|
|
207
|
+
const migrated = {
|
|
208
|
+
...parsed,
|
|
209
|
+
runner: {
|
|
210
|
+
...parsed.runner,
|
|
211
|
+
mode: 'bin',
|
|
212
|
+
bin_name: 'kitsune-rendercore'
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
writeIndexAtomic(migrated);
|
|
216
|
+
return migrated;
|
|
217
|
+
}
|
|
218
|
+
return parsed;
|
|
213
219
|
}
|
|
214
220
|
catch {
|
|
215
221
|
return defaultIndex();
|
|
@@ -824,48 +830,92 @@ function clampFloat(value, fallback, min, max = Number.MAX_SAFE_INTEGER) {
|
|
|
824
830
|
return fallback;
|
|
825
831
|
return Math.min(max, Math.max(min, n));
|
|
826
832
|
}
|
|
827
|
-
function
|
|
833
|
+
function mapProfileToQuality(profile) {
|
|
834
|
+
const p = clean(profile).toLowerCase();
|
|
835
|
+
if (p === 'performance')
|
|
836
|
+
return 'low';
|
|
837
|
+
if (p === 'balanced')
|
|
838
|
+
return 'medium';
|
|
839
|
+
if (p === 'quality')
|
|
840
|
+
return 'high';
|
|
841
|
+
if (p === 'low' || p === 'medium' || p === 'high' || p === 'ultra')
|
|
842
|
+
return p;
|
|
843
|
+
return 'high';
|
|
844
|
+
}
|
|
845
|
+
function normalizeApplyDefaults(raw, fallback) {
|
|
846
|
+
const src = (raw && typeof raw === 'object') ? raw : {};
|
|
847
|
+
const hwaccelRaw = clean(src.hwaccel).toLowerCase();
|
|
848
|
+
const qualityRaw = clean(src.quality).toLowerCase();
|
|
849
|
+
const profileRaw = src.profile;
|
|
850
|
+
const hwaccel = (hwaccelRaw === 'auto' || hwaccelRaw === 'nvdec' || hwaccelRaw === 'vaapi' || hwaccelRaw === 'none') ? hwaccelRaw : fallback.hwaccel;
|
|
851
|
+
const qualityFromProfile = mapProfileToQuality(profileRaw);
|
|
852
|
+
const quality = (qualityRaw === 'low' || qualityRaw === 'medium' || qualityRaw === 'high' || qualityRaw === 'ultra') ? qualityRaw : (profileRaw === undefined ? fallback.quality : qualityFromProfile);
|
|
828
853
|
return {
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
proxy_width: variant === '4k'
|
|
838
|
-
? clampInt(defaults.proxy_width_4k, 3840, 320)
|
|
839
|
-
: clampInt(defaults.proxy_width_hd, 1920, 320),
|
|
840
|
-
proxy_fps: clampInt(defaults.proxy_fps, 60, 1, 240),
|
|
841
|
-
proxy_crf: variant === '4k'
|
|
842
|
-
? clampInt(defaults.proxy_crf_4k, 16, 1, 51)
|
|
843
|
-
: clampInt(defaults.proxy_crf_hd, 18, 1, 51)
|
|
854
|
+
video_fps: clampInt(Number(src.video_fps ?? (src.display_fps === null ? fallback.video_fps : src.display_fps)), fallback.video_fps, 1, 240),
|
|
855
|
+
video_speed: clampFloat(Number(src.video_speed), fallback.video_speed, 0.1, 4.0),
|
|
856
|
+
hwaccel,
|
|
857
|
+
quality,
|
|
858
|
+
pause_on_steam_game: src.pause_on_steam_game === undefined
|
|
859
|
+
? fallback.pause_on_steam_game
|
|
860
|
+
: parseBoolish(String(src.pause_on_steam_game), fallback.pause_on_steam_game),
|
|
861
|
+
steam_poll_ms: clampInt(Number(src.steam_poll_ms), fallback.steam_poll_ms, 200, 120000)
|
|
844
862
|
};
|
|
845
863
|
}
|
|
846
|
-
function
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
864
|
+
function rendercoreEnvPath() {
|
|
865
|
+
return node_path_1.default.join(node_os_1.default.homedir(), '.config', 'kitsune-rendercore', 'env');
|
|
866
|
+
}
|
|
867
|
+
function syncRendercoreEnv(defaults) {
|
|
868
|
+
const p = rendercoreEnvPath();
|
|
869
|
+
(0, fs_1.ensureDir)(node_path_1.default.dirname(p));
|
|
870
|
+
const existing = {};
|
|
871
|
+
if (node_fs_1.default.existsSync(p)) {
|
|
872
|
+
const raw = node_fs_1.default.readFileSync(p, 'utf8');
|
|
873
|
+
for (const line of raw.split('\n')) {
|
|
874
|
+
const t = line.trim();
|
|
875
|
+
if (!t || t.startsWith('#'))
|
|
876
|
+
continue;
|
|
877
|
+
const eq = t.indexOf('=');
|
|
878
|
+
if (eq <= 0)
|
|
879
|
+
continue;
|
|
880
|
+
existing[t.slice(0, eq).trim()] = t.slice(eq + 1).trim();
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
existing.KRC_VIDEO_FPS = String(defaults.video_fps);
|
|
884
|
+
existing.KRC_VIDEO_SPEED = String(defaults.video_speed);
|
|
885
|
+
existing.KRC_HWACCEL = defaults.hwaccel;
|
|
886
|
+
existing.KRC_QUALITY = defaults.quality;
|
|
887
|
+
existing.KRC_PAUSE_ON_STEAM_GAME = defaults.pause_on_steam_game ? 'true' : 'false';
|
|
888
|
+
existing.KRC_STEAM_POLL_MS = String(defaults.steam_poll_ms);
|
|
889
|
+
const ordered = [
|
|
890
|
+
'KRC_VIDEO_DEFAULT',
|
|
891
|
+
'KRC_VIDEO_MAP_FILE',
|
|
892
|
+
'KRC_VIDEO_MAP',
|
|
893
|
+
'KRC_VIDEO_FPS',
|
|
894
|
+
'KRC_VIDEO_SPEED',
|
|
895
|
+
'KRC_HWACCEL',
|
|
896
|
+
'KRC_QUALITY',
|
|
897
|
+
'KRC_PAUSE_ON_STEAM_GAME',
|
|
898
|
+
'KRC_STEAM_POLL_MS'
|
|
899
|
+
];
|
|
900
|
+
const keys = Array.from(new Set([...ordered, ...Object.keys(existing)])).sort((a, b) => {
|
|
901
|
+
const ia = ordered.indexOf(a);
|
|
902
|
+
const ib = ordered.indexOf(b);
|
|
903
|
+
if (ia >= 0 && ib >= 0)
|
|
904
|
+
return ia - ib;
|
|
905
|
+
if (ia >= 0)
|
|
906
|
+
return -1;
|
|
907
|
+
if (ib >= 0)
|
|
908
|
+
return 1;
|
|
909
|
+
return a.localeCompare(b);
|
|
910
|
+
});
|
|
911
|
+
const lines = [];
|
|
912
|
+
for (const key of keys) {
|
|
913
|
+
const value = existing[key];
|
|
914
|
+
if (value === undefined)
|
|
915
|
+
continue;
|
|
916
|
+
lines.push(`${key}=${value}`);
|
|
917
|
+
}
|
|
918
|
+
node_fs_1.default.writeFileSync(p, `${lines.join('\n')}\n`, 'utf8');
|
|
869
919
|
}
|
|
870
920
|
async function resolvePost(provider, pageUrl) {
|
|
871
921
|
const html = await fetchHtml(pageUrl, pageUrl);
|
|
@@ -1039,26 +1089,6 @@ function parseId(id) {
|
|
|
1039
1089
|
throw new Error(`Invalid variant in id: ${id}`);
|
|
1040
1090
|
return { provider, slug, variant };
|
|
1041
1091
|
}
|
|
1042
|
-
async function buildApplyCommand(index, item, monitor) {
|
|
1043
|
-
const defs = index.apply_defaults;
|
|
1044
|
-
const proxyWidth = item.variant === '4k' ? 3840 : 1920;
|
|
1045
|
-
const proxyCrf = item.variant === '4k' ? defs.proxy_crf_4k : defs.proxy_crf_hd;
|
|
1046
|
-
const bool = (v) => (v ? 'true' : 'false');
|
|
1047
|
-
const args = [
|
|
1048
|
-
'video-play',
|
|
1049
|
-
item.file_path,
|
|
1050
|
-
'--monitor', monitor,
|
|
1051
|
-
'--profile', defs.profile,
|
|
1052
|
-
'--seamless-loop', bool(defs.seamless_loop),
|
|
1053
|
-
'--loop-crossfade', bool(defs.loop_crossfade),
|
|
1054
|
-
'--loop-crossfade-seconds', String(defs.loop_crossfade_seconds),
|
|
1055
|
-
'--optimize', bool(defs.optimize),
|
|
1056
|
-
'--proxy-width', String(proxyWidth),
|
|
1057
|
-
'--proxy-fps', String(defs.proxy_fps),
|
|
1058
|
-
'--proxy-crf', String(proxyCrf)
|
|
1059
|
-
];
|
|
1060
|
-
return { cmd: index.runner.bin_name, args };
|
|
1061
|
-
}
|
|
1062
1092
|
async function liveResolve(pageUrl) {
|
|
1063
1093
|
const provider = providerFromUrl(pageUrl);
|
|
1064
1094
|
const post = await resolvePost(provider, pageUrl);
|
|
@@ -1428,7 +1458,6 @@ async function liveFetch(opts) {
|
|
|
1428
1458
|
}
|
|
1429
1459
|
const id = itemId(provider, slug, selected.variant);
|
|
1430
1460
|
const thumbPath = await generateThumb(filePath, id);
|
|
1431
|
-
const defaults = readIndex().apply_defaults;
|
|
1432
1461
|
const item = {
|
|
1433
1462
|
id,
|
|
1434
1463
|
provider,
|
|
@@ -1442,25 +1471,19 @@ async function liveFetch(opts) {
|
|
|
1442
1471
|
size_bytes: sizeBytes,
|
|
1443
1472
|
favorite: false,
|
|
1444
1473
|
added_at: nowUnix(),
|
|
1445
|
-
last_applied_at: 0
|
|
1446
|
-
video_config: buildVideoConfigFromDefaults(defaults, selected.variant)
|
|
1474
|
+
last_applied_at: 0
|
|
1447
1475
|
};
|
|
1448
1476
|
withLiveLock(() => {
|
|
1449
1477
|
const current = readIndex();
|
|
1450
1478
|
const prev = current.items.find(v => v.id === item.id);
|
|
1451
|
-
const fallbackCfg = buildVideoConfigFromDefaults(current.apply_defaults, item.variant);
|
|
1452
1479
|
const next = prev
|
|
1453
1480
|
? {
|
|
1454
1481
|
...item,
|
|
1455
1482
|
favorite: prev.favorite,
|
|
1456
1483
|
added_at: prev.added_at || item.added_at,
|
|
1457
|
-
last_applied_at: prev.last_applied_at || 0
|
|
1458
|
-
video_config: normalizeVideoConfig(prev.video_config, fallbackCfg)
|
|
1484
|
+
last_applied_at: prev.last_applied_at || 0
|
|
1459
1485
|
}
|
|
1460
|
-
:
|
|
1461
|
-
...item,
|
|
1462
|
-
video_config: normalizeVideoConfig(item.video_config, fallbackCfg)
|
|
1463
|
-
};
|
|
1486
|
+
: item;
|
|
1464
1487
|
const updated = upsertItem(current, next);
|
|
1465
1488
|
writeIndexAtomic(updated);
|
|
1466
1489
|
return true;
|
|
@@ -1493,49 +1516,21 @@ async function liveApply(opts) {
|
|
|
1493
1516
|
if (!item || !node_fs_1.default.existsSync(item.file_path)) {
|
|
1494
1517
|
throw new Error(`Live file not found for ${idRaw}. Re-download it with live fetch.`);
|
|
1495
1518
|
}
|
|
1496
|
-
const fallbackCfg = buildVideoConfigFromDefaults(index.apply_defaults, item.variant);
|
|
1497
|
-
const cfg = normalizeVideoConfig(item.video_config, fallbackCfg);
|
|
1498
1519
|
const bin = index.runner.bin_name;
|
|
1499
1520
|
const setVideoArgs = [
|
|
1500
|
-
'config',
|
|
1501
1521
|
'set-video',
|
|
1502
1522
|
'--monitor', monitor,
|
|
1503
|
-
'--video', item.file_path
|
|
1504
|
-
'--keep-services', cfg.keep_services ? 'true' : 'false',
|
|
1505
|
-
'--profile', cfg.profile,
|
|
1506
|
-
'--loop-crossfade-seconds', String(cfg.loop_crossfade_seconds),
|
|
1507
|
-
'--proxy-width', String(cfg.proxy_width),
|
|
1508
|
-
'--proxy-fps', String(cfg.proxy_fps),
|
|
1509
|
-
'--proxy-crf', String(cfg.proxy_crf)
|
|
1523
|
+
'--video', item.file_path
|
|
1510
1524
|
];
|
|
1511
|
-
if (cfg.mute_audio)
|
|
1512
|
-
setVideoArgs.push('--mute-audio');
|
|
1513
|
-
if (cfg.display_fps !== null)
|
|
1514
|
-
setVideoArgs.push('--display-fps', String(cfg.display_fps));
|
|
1515
|
-
if (cfg.seamless_loop)
|
|
1516
|
-
setVideoArgs.push('--seamless-loop');
|
|
1517
|
-
if (cfg.loop_crossfade)
|
|
1518
|
-
setVideoArgs.push('--loop-crossfade');
|
|
1519
|
-
if (cfg.optimize)
|
|
1520
|
-
setVideoArgs.push('--optimize');
|
|
1521
1525
|
await (0, exec_1.run)(bin, setVideoArgs, { timeoutMs: 120000 });
|
|
1522
|
-
//
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
}
|
|
1526
|
-
|
|
1527
|
-
try {
|
|
1528
|
-
await (0, exec_1.run)(bin, ['service-autostart', 'enable'], { timeoutMs: 15000 });
|
|
1529
|
-
}
|
|
1530
|
-
catch { }
|
|
1531
|
-
try {
|
|
1532
|
-
await (0, exec_1.run)(bin, ['stop-services'], { timeoutMs: 15000 });
|
|
1533
|
-
}
|
|
1534
|
-
catch { }
|
|
1535
|
-
await (0, exec_1.run)(bin, ['start-config'], { timeoutMs: 120000 });
|
|
1526
|
+
// Live mode owns wallpaper state: stop static rotation services while live is active.
|
|
1527
|
+
await (0, workshop_1.workshopCoexistenceEnter)();
|
|
1528
|
+
// Keep live authority persistent across session restarts.
|
|
1529
|
+
await (0, exec_1.run)(bin, ['service', 'install'], { timeoutMs: 20000 });
|
|
1530
|
+
await (0, exec_1.run)(bin, ['service', 'enable'], { timeoutMs: 20000 });
|
|
1536
1531
|
withLiveLock(() => {
|
|
1537
1532
|
const current = readIndex();
|
|
1538
|
-
const updatedItems = current.items.map(v => (v.id === item.id ? { ...v, last_applied_at: nowUnix()
|
|
1533
|
+
const updatedItems = current.items.map(v => (v.id === item.id ? { ...v, last_applied_at: nowUnix() } : v));
|
|
1539
1534
|
const perMonitor = { ...current.per_monitor };
|
|
1540
1535
|
const currentMon = perMonitor[monitor] || {
|
|
1541
1536
|
auto_apply: false,
|
|
@@ -1555,7 +1550,7 @@ async function liveApply(opts) {
|
|
|
1555
1550
|
monitor,
|
|
1556
1551
|
runner: index.runner.mode,
|
|
1557
1552
|
command: bin,
|
|
1558
|
-
args:
|
|
1553
|
+
args: setVideoArgs
|
|
1559
1554
|
};
|
|
1560
1555
|
}
|
|
1561
1556
|
function liveFavorite(id, on) {
|
|
@@ -1685,8 +1680,8 @@ async function liveDoctor(opts) {
|
|
|
1685
1680
|
}
|
|
1686
1681
|
if (opts?.fix && deps.runner_bin) {
|
|
1687
1682
|
try {
|
|
1688
|
-
await (0, exec_1.run)(index.runner.bin_name, ['install-
|
|
1689
|
-
fix.push(`Executed: ${index.runner.bin_name} install-
|
|
1683
|
+
await (0, exec_1.run)(index.runner.bin_name, ['install-deps'], { timeoutMs: 180000 });
|
|
1684
|
+
fix.push(`Executed: ${index.runner.bin_name} install-deps`);
|
|
1690
1685
|
try {
|
|
1691
1686
|
await (0, exec_1.run)('ffmpeg', ['-version'], { timeoutMs: 4000 });
|
|
1692
1687
|
deps.ffmpeg = true;
|
|
@@ -1703,11 +1698,11 @@ async function liveDoctor(opts) {
|
|
|
1703
1698
|
}
|
|
1704
1699
|
}
|
|
1705
1700
|
catch (e) {
|
|
1706
|
-
fix.push(`Failed to execute '${index.runner.bin_name} install-
|
|
1701
|
+
fix.push(`Failed to execute '${index.runner.bin_name} install-deps': ${String(e)}`);
|
|
1707
1702
|
}
|
|
1708
1703
|
}
|
|
1709
1704
|
else if (!deps.ffmpeg || !deps.hyprctl) {
|
|
1710
|
-
fix.push(`Run '${index.runner.bin_name} install-
|
|
1705
|
+
fix.push(`Run '${index.runner.bin_name} install-deps' to install missing runtime dependencies`);
|
|
1711
1706
|
}
|
|
1712
1707
|
return {
|
|
1713
1708
|
ok: Object.values(deps).every(Boolean) || (deps.hyprctl === false && Object.keys(deps).filter(k => k !== 'hyprctl').every(k => deps[k])),
|
|
@@ -1717,6 +1712,71 @@ async function liveDoctor(opts) {
|
|
|
1717
1712
|
fix
|
|
1718
1713
|
};
|
|
1719
1714
|
}
|
|
1715
|
+
async function liveServiceAutostart(action) {
|
|
1716
|
+
const index = readIndex();
|
|
1717
|
+
let bin = index.runner.bin_name;
|
|
1718
|
+
let out;
|
|
1719
|
+
const extractResult = (err) => {
|
|
1720
|
+
const result = err?.result;
|
|
1721
|
+
if (!result)
|
|
1722
|
+
return null;
|
|
1723
|
+
return {
|
|
1724
|
+
stdout: String(result.stdout ?? ''),
|
|
1725
|
+
stderr: String(result.stderr ?? ''),
|
|
1726
|
+
code: Number(result.code ?? 1)
|
|
1727
|
+
};
|
|
1728
|
+
};
|
|
1729
|
+
try {
|
|
1730
|
+
out = await (0, exec_1.run)(bin, ['service', action], { timeoutMs: 30000 });
|
|
1731
|
+
}
|
|
1732
|
+
catch (err) {
|
|
1733
|
+
const partial = extractResult(err);
|
|
1734
|
+
if (action === 'status' && partial) {
|
|
1735
|
+
out = partial;
|
|
1736
|
+
return {
|
|
1737
|
+
ok: true,
|
|
1738
|
+
action,
|
|
1739
|
+
runner: bin,
|
|
1740
|
+
stdout: out.stdout.trim(),
|
|
1741
|
+
stderr: out.stderr.trim(),
|
|
1742
|
+
code: out.code
|
|
1743
|
+
};
|
|
1744
|
+
}
|
|
1745
|
+
// Legacy compatibility: if user still points to kitsune-livewallpaper,
|
|
1746
|
+
// map to its older subcommand to avoid hard failure.
|
|
1747
|
+
if (clean(bin) === 'kitsune-livewallpaper') {
|
|
1748
|
+
try {
|
|
1749
|
+
out = await (0, exec_1.run)(bin, ['service-autostart', action], { timeoutMs: 30000 });
|
|
1750
|
+
}
|
|
1751
|
+
catch (legacyErr) {
|
|
1752
|
+
const legacyPartial = extractResult(legacyErr);
|
|
1753
|
+
if (action === 'status' && legacyPartial) {
|
|
1754
|
+
out = legacyPartial;
|
|
1755
|
+
return {
|
|
1756
|
+
ok: true,
|
|
1757
|
+
action,
|
|
1758
|
+
runner: bin,
|
|
1759
|
+
stdout: out.stdout.trim(),
|
|
1760
|
+
stderr: out.stderr.trim(),
|
|
1761
|
+
code: out.code
|
|
1762
|
+
};
|
|
1763
|
+
}
|
|
1764
|
+
throw legacyErr;
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
else {
|
|
1768
|
+
throw err;
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
return {
|
|
1772
|
+
ok: true,
|
|
1773
|
+
action,
|
|
1774
|
+
runner: bin,
|
|
1775
|
+
stdout: out.stdout.trim(),
|
|
1776
|
+
stderr: out.stderr.trim(),
|
|
1777
|
+
code: out.code
|
|
1778
|
+
};
|
|
1779
|
+
}
|
|
1720
1780
|
function liveSetRunner(opts) {
|
|
1721
1781
|
return withLiveLock(() => {
|
|
1722
1782
|
const current = readIndex();
|
|
@@ -1730,36 +1790,12 @@ function liveSetRunner(opts) {
|
|
|
1730
1790
|
});
|
|
1731
1791
|
}
|
|
1732
1792
|
function liveGetWallpaperConfig(id) {
|
|
1733
|
-
|
|
1734
|
-
if (!itemIdValue)
|
|
1735
|
-
throw new Error('id is required');
|
|
1736
|
-
const index = readIndex();
|
|
1737
|
-
const item = index.items.find(v => v.id === itemIdValue);
|
|
1738
|
-
if (!item)
|
|
1739
|
-
throw new Error(`Live item not found: ${itemIdValue}`);
|
|
1740
|
-
const fallback = buildVideoConfigFromDefaults(index.apply_defaults, item.variant);
|
|
1741
|
-
return {
|
|
1742
|
-
ok: true,
|
|
1743
|
-
id: item.id,
|
|
1744
|
-
video_config: normalizeVideoConfig(item.video_config, fallback)
|
|
1745
|
-
};
|
|
1793
|
+
throw new Error('Per-wallpaper config is no longer supported. Use: live config apply-defaults');
|
|
1746
1794
|
}
|
|
1747
1795
|
function liveSetWallpaperConfig(id, opts) {
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
return withLiveLock(() => {
|
|
1752
|
-
const current = readIndex();
|
|
1753
|
-
const item = current.items.find(v => v.id === itemIdValue);
|
|
1754
|
-
if (!item)
|
|
1755
|
-
throw new Error(`Live item not found: ${itemIdValue}`);
|
|
1756
|
-
const fallback = buildVideoConfigFromDefaults(current.apply_defaults, item.variant);
|
|
1757
|
-
const previous = normalizeVideoConfig(item.video_config, fallback);
|
|
1758
|
-
const nextCfg = normalizeVideoConfig({ ...previous, ...opts }, fallback);
|
|
1759
|
-
const items = current.items.map(v => (v.id === itemIdValue ? { ...v, video_config: nextCfg } : v));
|
|
1760
|
-
writeIndexAtomic({ ...current, items });
|
|
1761
|
-
return { ok: true, id: itemIdValue, video_config: nextCfg };
|
|
1762
|
-
});
|
|
1796
|
+
void id;
|
|
1797
|
+
void opts;
|
|
1798
|
+
throw new Error('Per-wallpaper config is no longer supported. Use: live config apply-defaults');
|
|
1763
1799
|
}
|
|
1764
1800
|
function liveGetConfig() {
|
|
1765
1801
|
const index = readIndex();
|
|
@@ -1768,34 +1804,12 @@ function liveGetConfig() {
|
|
|
1768
1804
|
function liveSetApplyDefaults(opts) {
|
|
1769
1805
|
return withLiveLock(() => {
|
|
1770
1806
|
const current = readIndex();
|
|
1771
|
-
const next = {
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
next.mute_audio = opts.mute_audio;
|
|
1776
|
-
if (opts.profile !== undefined)
|
|
1777
|
-
next.profile = opts.profile;
|
|
1778
|
-
if (opts.display_fps !== undefined)
|
|
1779
|
-
next.display_fps = opts.display_fps;
|
|
1780
|
-
if (opts.seamless_loop !== undefined)
|
|
1781
|
-
next.seamless_loop = opts.seamless_loop;
|
|
1782
|
-
if (opts.loop_crossfade !== undefined)
|
|
1783
|
-
next.loop_crossfade = opts.loop_crossfade;
|
|
1784
|
-
if (opts.loop_crossfade_seconds !== undefined)
|
|
1785
|
-
next.loop_crossfade_seconds = opts.loop_crossfade_seconds;
|
|
1786
|
-
if (opts.optimize !== undefined)
|
|
1787
|
-
next.optimize = opts.optimize;
|
|
1788
|
-
if (opts.proxy_width_hd !== undefined)
|
|
1789
|
-
next.proxy_width_hd = opts.proxy_width_hd;
|
|
1790
|
-
if (opts.proxy_width_4k !== undefined)
|
|
1791
|
-
next.proxy_width_4k = opts.proxy_width_4k;
|
|
1792
|
-
if (opts.proxy_fps !== undefined)
|
|
1793
|
-
next.proxy_fps = opts.proxy_fps;
|
|
1794
|
-
if (opts.proxy_crf_hd !== undefined)
|
|
1795
|
-
next.proxy_crf_hd = opts.proxy_crf_hd;
|
|
1796
|
-
if (opts.proxy_crf_4k !== undefined)
|
|
1797
|
-
next.proxy_crf_4k = opts.proxy_crf_4k;
|
|
1807
|
+
const next = normalizeApplyDefaults({
|
|
1808
|
+
...current.apply_defaults,
|
|
1809
|
+
...opts
|
|
1810
|
+
}, current.apply_defaults);
|
|
1798
1811
|
writeIndexAtomic({ ...current, apply_defaults: next });
|
|
1812
|
+
syncRendercoreEnv(next);
|
|
1799
1813
|
return { ok: true, apply_defaults: next };
|
|
1800
1814
|
});
|
|
1801
1815
|
}
|
package/dist/core/workshop.js
CHANGED
|
@@ -131,9 +131,12 @@ function getCoexistServices() {
|
|
|
131
131
|
const defaults = [
|
|
132
132
|
'swww-daemon.service',
|
|
133
133
|
'swww-daemon@kitowall.service',
|
|
134
|
+
'kitowall-watch.service',
|
|
135
|
+
'kitowall-next.service',
|
|
136
|
+
'kitowall-next.timer',
|
|
134
137
|
'hyprwall-watch.service',
|
|
135
|
-
'hyprwall-next.
|
|
136
|
-
'
|
|
138
|
+
'hyprwall-next.service',
|
|
139
|
+
'hyprwall-next.timer'
|
|
137
140
|
];
|
|
138
141
|
const configured = Array.isArray(cfg.coexistServices) ? cfg.coexistServices.map(v => String(v).trim()).filter(Boolean) : [];
|
|
139
142
|
return configured.length > 0 ? configured : defaults;
|
|
@@ -956,6 +959,29 @@ function killPid(pid) {
|
|
|
956
959
|
return false;
|
|
957
960
|
}
|
|
958
961
|
}
|
|
962
|
+
async function stopKnownLiveProcesses() {
|
|
963
|
+
// Live V2 can run wallpapers without registering pids in workshop active-state.
|
|
964
|
+
// Stop common runtime processes as best-effort fallback.
|
|
965
|
+
const patterns = [
|
|
966
|
+
'mpvpaper',
|
|
967
|
+
'kitsune-livewallpaper',
|
|
968
|
+
'kitsune-rendercore'
|
|
969
|
+
];
|
|
970
|
+
for (const pattern of patterns) {
|
|
971
|
+
try {
|
|
972
|
+
await (0, exec_1.run)('pkill', ['-f', pattern]);
|
|
973
|
+
}
|
|
974
|
+
catch {
|
|
975
|
+
// best effort
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
try {
|
|
979
|
+
await (0, exec_1.run)('systemctl', ['--user', 'stop', 'kitsune-rendercore.service']);
|
|
980
|
+
}
|
|
981
|
+
catch {
|
|
982
|
+
// best effort
|
|
983
|
+
}
|
|
984
|
+
}
|
|
959
985
|
function workshopActiveStatus() {
|
|
960
986
|
const paths = getWePaths();
|
|
961
987
|
ensureWePaths(paths);
|
|
@@ -982,14 +1008,26 @@ async function isUnitActive(unit) {
|
|
|
982
1008
|
return false;
|
|
983
1009
|
}
|
|
984
1010
|
}
|
|
1011
|
+
async function isUnitEnabled(unit) {
|
|
1012
|
+
try {
|
|
1013
|
+
const out = await (0, exec_1.run)('systemctl', ['--user', 'is-enabled', unit], { timeoutMs: 4000 });
|
|
1014
|
+
return out.stdout.trim() === 'enabled';
|
|
1015
|
+
}
|
|
1016
|
+
catch {
|
|
1017
|
+
return false;
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
985
1020
|
async function workshopCoexistenceEnter() {
|
|
986
1021
|
const paths = getWePaths();
|
|
987
1022
|
ensureWePaths(paths);
|
|
988
1023
|
const units = getCoexistServices();
|
|
989
1024
|
const active = [];
|
|
1025
|
+
const enabled = [];
|
|
990
1026
|
for (const unit of units) {
|
|
991
1027
|
if (await isUnitActive(unit))
|
|
992
1028
|
active.push(unit);
|
|
1029
|
+
if (await isUnitEnabled(unit))
|
|
1030
|
+
enabled.push(unit);
|
|
993
1031
|
}
|
|
994
1032
|
for (const unit of active) {
|
|
995
1033
|
try {
|
|
@@ -999,8 +1037,16 @@ async function workshopCoexistenceEnter() {
|
|
|
999
1037
|
// best effort
|
|
1000
1038
|
}
|
|
1001
1039
|
}
|
|
1040
|
+
for (const unit of enabled) {
|
|
1041
|
+
try {
|
|
1042
|
+
await (0, exec_1.run)('systemctl', ['--user', 'disable', unit]);
|
|
1043
|
+
}
|
|
1044
|
+
catch {
|
|
1045
|
+
// best effort
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1002
1048
|
const snapshotId = String(now());
|
|
1003
|
-
const snap = { id: snapshotId, ts: now(), active };
|
|
1049
|
+
const snap = { id: snapshotId, ts: now(), active, enabled };
|
|
1004
1050
|
(0, fs_1.writeJson)(node_path_1.default.join(snapshotDir(paths), `${snapshotId}.json`), snap);
|
|
1005
1051
|
(0, fs_1.writeJson)(snapshotFile(paths), { id: snapshotId, ts: snap.ts });
|
|
1006
1052
|
return { ok: true, stopped: active, snapshot: active, snapshot_id: snapshotId };
|
|
@@ -1013,18 +1059,29 @@ async function workshopCoexistenceExit() {
|
|
|
1013
1059
|
return { ok: true, restored: [] };
|
|
1014
1060
|
const raw = JSON.parse(node_fs_1.default.readFileSync(snapPath, 'utf8'));
|
|
1015
1061
|
let active = Array.isArray(raw.active) ? raw.active.map(v => String(v)) : [];
|
|
1062
|
+
let enabled = Array.isArray(raw.enabled) ? raw.enabled.map(v => String(v)) : [];
|
|
1016
1063
|
if ((!active || active.length === 0) && raw.id) {
|
|
1017
1064
|
const historical = node_path_1.default.join(snapshotDir(paths), `${raw.id}.json`);
|
|
1018
1065
|
if (node_fs_1.default.existsSync(historical)) {
|
|
1019
1066
|
try {
|
|
1020
1067
|
const snap = JSON.parse(node_fs_1.default.readFileSync(historical, 'utf8'));
|
|
1021
1068
|
active = Array.isArray(snap.active) ? snap.active.map(v => String(v)) : [];
|
|
1069
|
+
enabled = Array.isArray(snap.enabled) ? snap.enabled.map(v => String(v)) : [];
|
|
1022
1070
|
}
|
|
1023
1071
|
catch {
|
|
1024
1072
|
active = [];
|
|
1073
|
+
enabled = [];
|
|
1025
1074
|
}
|
|
1026
1075
|
}
|
|
1027
1076
|
}
|
|
1077
|
+
for (const unit of enabled) {
|
|
1078
|
+
try {
|
|
1079
|
+
await (0, exec_1.run)('systemctl', ['--user', 'enable', unit]);
|
|
1080
|
+
}
|
|
1081
|
+
catch {
|
|
1082
|
+
// best effort
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1028
1085
|
const restored = [];
|
|
1029
1086
|
for (const unit of active) {
|
|
1030
1087
|
try {
|
|
@@ -1086,9 +1143,13 @@ async function workshopStop(options) {
|
|
|
1086
1143
|
}
|
|
1087
1144
|
}
|
|
1088
1145
|
else if (options?.all) {
|
|
1146
|
+
await stopKnownLiveProcesses();
|
|
1089
1147
|
clearActiveState();
|
|
1090
1148
|
shouldRestore = true;
|
|
1091
1149
|
}
|
|
1150
|
+
if (options?.all) {
|
|
1151
|
+
await stopKnownLiveProcesses();
|
|
1152
|
+
}
|
|
1092
1153
|
const coexist = shouldRestore ? await workshopCoexistenceExit() : { ok: true, restored: [] };
|
|
1093
1154
|
return {
|
|
1094
1155
|
ok: true,
|
package/dist/utils/exec.js
CHANGED