dsh-mpkg-wallpaper 3.11.0 → 3.12.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/README.en.md CHANGED
@@ -441,7 +441,7 @@ This section gathers what environment and what sizes work, and what is out of re
441
441
 
442
442
  ### Without a Wallpaper Engine install (missing / non-Windows)
443
443
 
444
- A "WE install" means the Steam edition of Wallpaper Engine (appid **431960**). The host's `locateWallpaperEngine()` (`lib/index.js:303-327`) searches in this order: Windows registry `HKCU\Software\Valve\Steam\SteamPath` → common Steam directories → non-Windows Steam directories (macOS `~/Library/Application Support/Steam`, Linux/Android `~/.local/share/Steam`, WSL `/mnt/c/...`) → every library's `steamapps/libraryfolders.vdf` containing 431960 → and only accepts the one where `<library>/steamapps/common/wallpaper_engine/wallpaper32.exe` exists. **If nothing is found it returns `null`** and everything falls back:
444
+ A "WE install" means the Steam edition of Wallpaper Engine (appid **431960**). The host's `locateWallpaperEngine()` (`lib/index.js:311-399`) searches in this order: Windows registry `HKCU\Software\Valve\Steam\SteamPath` → common Steam directories → non-Windows Steam directories (macOS `~/Library/Application Support/Steam`, Linux/Android `~/.local/share/Steam`, WSL `/mnt/c/...` plus a per-drive enumeration of `/mnt/d`…`/mnt/z`) → every library's `steamapps/libraryfolders.vdf` containing 431960 → and only accepts the one where `<library>/steamapps/common/wallpaper_engine/wallpaper32.exe` exists. **If nothing is found it returns `null`** and everything falls back:
445
445
 
446
446
  | Scenario | Real behaviour (with code location) |
447
447
  |---|---|
package/README.md CHANGED
@@ -442,7 +442,7 @@ node tools/build-bundle.mjs
442
442
 
443
443
  ### 没有 Wallpaper Engine 安装时(缺失 / 非 Windows)
444
444
 
445
- 「WE 安装」指 Steam 版 Wallpaper Engine(appid **431960**)。宿主端 `locateWallpaperEngine()`(`lib/index.js:303-327`)按这个顺序找:Windows 注册表 `HKCU\Software\Valve\Steam\SteamPath` → 常见 Steam 目录 → 非 Windows 的 Steam 目录(macOS `~/Library/Application Support/Steam`、Linux/Android `~/.local/share/Steam`、WSL `/mnt/c/...`)→ 各库 `steamapps/libraryfolders.vdf` 里含 431960 的库 → 只认 `<库>/steamapps/common/wallpaper_engine/wallpaper32.exe` 存在的那一个。**找不到就返回 `null`**,后续走降级路径:
445
+ 「WE 安装」指 Steam 版 Wallpaper Engine(appid **431960**)。宿主端 `locateWallpaperEngine()`(`lib/index.js:311-399`)按这个顺序找:Windows 注册表 `HKCU\Software\Valve\Steam\SteamPath` → 常见 Steam 目录 → 非 Windows 的 Steam 目录(macOS `~/Library/Application Support/Steam`、Linux/Android `~/.local/share/Steam`、WSL `/mnt/c/...` 与按盘符枚举的 `/mnt/d`…`/mnt/z`)→ 各库 `steamapps/libraryfolders.vdf` 里含 431960 的库 → 只认 `<库>/steamapps/common/wallpaper_engine/wallpaper32.exe` 存在的那一个。**找不到就返回 `null`**,后续走降级路径:
446
446
 
447
447
  | 场景 | 真实行为(含代码位置) |
448
448
  |---|---|
@@ -611,8 +611,11 @@ node tools/style-scope-guard.mjs # 判据是末行「… OK / … ALLOWLISTED
611
611
 
612
612
  ## 这一版新增/变更
613
613
 
614
- 当前版本 = `package.json` 的 **3.11.0**(本轮发布线 3.8.0 → 3.8.1 → 3.8.2 → 3.9.0 → 3.9.1 → 3.10.0 → 3.10.1 → **3.11.0**)。发布前置/命令/回滚见 [`docs/RELEASE.md`](docs/RELEASE.md),本轮改动清单见 [`docs/RELEASE-READY-3.8.0.md`](docs/RELEASE-READY-3.8.0.md),Now playing / 壁纸声音的根因与真机读数见 [`docs/NOW-PLAYING-DSH.md`](docs/NOW-PLAYING-DSH.md) §7.7。
614
+ 当前版本 = `package.json` 的 **3.12.0**(本轮发布线 3.8.0 → 3.8.1 → 3.8.2 → 3.9.0 → 3.9.1 → 3.10.0 → 3.10.1 → 3.11.0 → **3.12.0**)。发布前置/命令/回滚见 [`docs/RELEASE.md`](docs/RELEASE.md),本轮改动清单见 [`docs/RELEASE-READY-3.8.0.md`](docs/RELEASE-READY-3.8.0.md),Now playing / 壁纸声音的根因与真机读数见 [`docs/NOW-PLAYING-DSH.md`](docs/NOW-PLAYING-DSH.md) §7.7。
615
615
 
616
+ - **3.12.0:WSL 上 Steam 装在别的盘也能扫到(安装目录候选抽成可注入纯函数 + 盘符枚举)+ 插件侧跨平台静态门禁** ——
617
+ ①真漏网:WSL 分支原来只写死 `/mnt/c/...`,Steam 装在 D:/E:/… 的 WSL 用户**永远扫不到**(Windows 与 WSL 双装、Steam 装 D 盘都很常见)。现在 `lib/index.js` 的 `steamProbeDirs(env)`(**纯函数**,`platform`/`release`/`homedir`/`exists`/`procVersion` 全可注入)在 `platform === 'linux'` 且判为 WSL 时枚举 `/mnt/d` … `/mnt/z` × `Program Files (x86)/Steam`、`Program Files/Steam`、`SteamLibrary`、`Steam` 四个相对路径;WSL 判据两条(`os.release()` 含 `microsoft`,老 WSL1 再读 `/proc/version`,读失败一律吞掉不抛)。非 WSL 的 linux/darwin **不**产出 `/mnt/d…z`;win32/darwin/linux/android 四个平台的候选集合与改动前**逐字节相同**(门禁逐项断言)。
618
+ ②新增 `tools/cross-platform-test.mjs`(**63 断言 / 1.2s**,无浏览器无网络;登记进 `tools/check.sh` 的既有第 6 步,**不新增 step 编号**):WSL 分支可判(在非 WSL 机器上用注入入参断言 —— 含 `/mnt/d/SteamLibrary`,换普通内核后 0 条 `/mnt/d…z`)、三平台各自成根且互不串台、纯函数纪律(确定性/无重复/退化入参不抛)、代码里的 `/tmp` 字面量与五类宿主绝对路径必须"同行可覆盖"(存量 25 处走逐条带理由的账本,**每条反查必须仍命中**,腐烂即判红)、shell 可移植(9 种 bash 4+ 写法 + shebang 与语法匹配)、文件名可移植、BOM/CRLF、G 段逐类合成反例必红 + 干净样本零发现,以及两处**变异自证**(删掉盘符枚举 ⇒ 判红;把路径写死 ⇒ 判红)。
616
619
  - **3.11.0:音乐卡片 seek 落错元素(真机 P0)+ 音频格式路由 + 预览框几何 + 网页帧三档 + 风险预检接口** ——
617
620
  ①**seek 落错元素**(真机:卡片显示总时长 `−3:22`、点轨道 90% 视频只从 11.53 → 12.69):根因两条 —— `npActiveVideo` 把 `display:none` 当成"这不是壁纸媒体"(容器档换档窗口里画面确实不可见,于是卡片掉进曲目分支、绑到**上一张壁纸残留的游离 `<audio>`**),且 `npTransport("seek")` 会"顺手"也 seek 那个游离 audio、`npSyncAudio` 的 `timeupdate` 又**无条件**把 audio 的时钟写进同一根进度条。现在卡片的**数据源/时长/进度/seek/静音/上报全部走唯一目标** `npMediaTarget()`(`{el, kind:'video'|'audio'|'none'}`,判据 = src 非空 + 视频档 + 非 web 档;残留 audio 只在**这张壁纸真有曲目清单**时才认);判据 `tools/np-control-test.mjs` T 组 9 条 + **2 条变异自证**(改回旧写法 ⇒ T 组红 6/1 条),真机探针 `tools/np-seek-live-probe.mjs`(`--selftest` 8/0;**真机那一次要等宿主重启后同步生效才跑**,见诚实清单)。
618
621
  ②**更多音频格式**:真凶在**路由**而不是扫描表 —— `/raw` 对非 mpkg/pkg/json 一律 `application/octet-stream`、`/custom-media` 没有音频分支、`/custom-dir` 白名单没有音频后缀,另有 `AUDIO_MAGIC_MIME` 的 ISO-BMFF 分支写成 `fByp`(ftyp 在偏移 4)且整体被 `n>=12` 包住。现在**唯一表** `AUDIO_SUFFIX_MIME`(`.mp3/.ogg/.oga/.opus/.wav/.flac/.m4a/.aac`,与渲染器 `server/upload-policy.mjs` 同一份名单)+ `audioMimeFor(path, head)`(magic 优先 / 后缀兜底 / 带 ID3 前缀的 `.flac` 例外),四条路由改查表(`audio-scan` 79/0、`scene-audio-route` 37/0,实测 `/raw` 与 `/custom-folder` 的 `.flac` 都是 `audio/flac`)。
package/lib/index.js CHANGED
@@ -308,20 +308,71 @@ function steamPathFromRegistry() {
308
308
  } catch { return null; }
309
309
  }
310
310
 
311
+ /** ③ 自动发现:Steam 安装目录**候选**(纯函数:只产出字符串,是否存在由调用方 existsSync 决定)。
312
+ *
313
+ * 为什么必须是纯函数:平台分支(尤其 WSL 的盘符枚举)要能在**非 WSL 机器**上断言 ——
314
+ * "判断是契约,不是在我机器上能跑"。入参 `{ platform, release, homedir, exists, procVersion }`
315
+ * 全部可注入,缺省分别取 `process.platform` / `os.release()` / `os.homedir()`;
316
+ * 返回数组全是字符串、无重复、顺序稳定(同一入参两次调用逐项相同)。
317
+ *
318
+ * ①(2026-09-21 跨平台轮) 修一个真实漏网:WSL 分支原来只写死 `/mnt/c/...` ⇒ Steam 装在 D:/E:/…
319
+ * 的 WSL 用户**永远扫不到**(Windows 与 WSL 双装是常态,Steam 装在 D 盘也很常见)。
320
+ * 现在按盘符枚举 `/mnt/d` … `/mnt/z` × 四个相对路径(见 WSL_STEAM_RELS)。
321
+ *
322
+ * WSL 判据两条:`release` 小写含 `microsoft`(WSL2 内核固定带这个字样);老 WSL1 内核 release
323
+ * 里没有 ⇒ 再读 `/proc/version`(用 `exists` 先短路,读失败/权限拒绝一律吞掉,不抛)。
324
+ * 只有 `platform === 'linux'` 才走这条分支:win32/darwin 上 release 出现 microsoft 属于不可能的
325
+ * 输入,不去猜(否则等于到处乱探)。
326
+ *
327
+ * ⚠ `STEAM_PROBE_DIRS` 与下面两条 `/mnt/c/...` 都是**历史遗留的不分平台候选项**(在 Windows/macOS
328
+ * 上就是两个不存在的字符串,existsSync 自然为 false)。保持原样 ⇒ win32/darwin/linux 三个平台的
329
+ * 候选集合与改动前**逐字节相同**;门禁 tools/cross-platform-test.mjs 逐项断言这一点。
330
+ */
331
+ const WSL_DRIVE_LETTERS = 'defghijklmnopqrstuvwxyz'; // /mnt/d … /mnt/z(C 盘单独两条,历史上就有)
332
+ const WSL_STEAM_RELS = ['Program Files (x86)/Steam', 'Program Files/Steam', 'SteamLibrary', 'Steam'];
333
+ export function steamProbeDirs(env = {}) {
334
+ const platform = typeof env.platform === 'string' ? env.platform : process.platform;
335
+ const homedir = typeof env.homedir === 'string' ? env.homedir : homedirSafe();
336
+ const release = typeof env.release === 'string' ? env.release : releaseSafe();
337
+ // exists 只用来**短路** /proc/version 的读取(Windows/macOS 上省一次注定失败的 open);
338
+ // 传进来的实现抛异常也必须被吞掉(门禁里有一条"给会抛的 exists 也不许影响返回")。
339
+ const exists = typeof env.exists === 'function' ? env.exists : existsSync;
340
+ const dirs = [...STEAM_PROBE_DIRS];
341
+ if (platform === 'darwin') dirs.push(join(homedir, 'Library', 'Application Support', 'Steam'));
342
+ if (platform === 'linux' || platform === 'android') dirs.push(join(homedir, '.local', 'share', 'Steam'));
343
+ dirs.push('/mnt/c/Program Files (x86)/Steam', '/mnt/c/Program Files/Steam');
344
+ if (platform === 'linux' && isWsl(release, env, exists)) {
345
+ for (const letter of WSL_DRIVE_LETTERS) for (const rel of WSL_STEAM_RELS) dirs.push('/mnt/' + letter + '/' + rel);
346
+ }
347
+ return [...new Set(dirs)];
348
+ }
349
+ /** `os.homedir()` / `os.release()` 在个别残缺环境里会抛(部分容器没有 passwd 项)⇒ 吞掉给兜底值,
350
+ * 保证 steamProbeDirs() 永不抛(判据里"退化入参也不许抛"包含本机 os 调用这一路)。 */
351
+ function homedirSafe() { try { return os.homedir() || '.'; } catch { return '.'; } }
352
+ function releaseSafe() { try { return os.release() || ''; } catch { return ''; } }
353
+ /** WSL 判定(判据见 steamProbeDirs 注释)。`env.procVersion` 可注入 ⇒ 不依赖本机 /proc/version。 */
354
+ function isWsl(release, env, exists) {
355
+ if (String(release).toLowerCase().includes('microsoft')) return true;
356
+ try {
357
+ if (env.procVersion !== undefined) {
358
+ const v = typeof env.procVersion === 'function' ? env.procVersion() : env.procVersion;
359
+ return String(v == null ? '' : v).toLowerCase().includes('microsoft');
360
+ }
361
+ if (!exists('/proc/version')) return false;
362
+ return readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft');
363
+ } catch { return false; }
364
+ }
365
+
311
366
  /** ③ 自动发现:定位壁纸引擎安装目录(Steam libraryfolders.vdf + 常见路径探测)。
312
367
  * ①(修正) 补非 Windows 平台探测:macOS/Linux/WSL 也各自有 Steam 安装路径。
368
+ * 候选清单本身在纯函数 steamProbeDirs() 里(平台分支都在那里,可注入、可在非 WSL 机器上断言);
369
+ * 本函数只负责"注册表优先 → 逐个候选读 libraryfolders.vdf → 只认 wallpaper32.exe 存在的那一个"。
313
370
  * 路径是纯字符串,平台用不到时 existsSync 自然为 false,无副作用。 */
314
- const STEAM_PROBE_DIRS_NONWIN = [
315
- ...(process.platform === 'darwin' ? [join(os.homedir(), 'Library', 'Application Support', 'Steam')] : []),
316
- ...(process.platform === 'linux' || process.platform === 'android' ? [join(os.homedir(), '.local', 'share', 'Steam')] : []),
317
- '/mnt/c/Program Files (x86)/Steam', // WSL
318
- '/mnt/c/Program Files/Steam', // WSL
319
- ];
320
371
  function locateWallpaperEngine() {
321
372
  const probes = [];
322
373
  const reg = steamPathFromRegistry();
323
374
  if (reg) probes.push(reg);
324
- probes.push(...STEAM_PROBE_DIRS, ...STEAM_PROBE_DIRS_NONWIN);
375
+ probes.push(...steamProbeDirs());
325
376
  const libraries = [];
326
377
  for (const probe of probes) {
327
378
  const vdf = join(probe, 'steamapps', 'libraryfolders.vdf');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-mpkg-wallpaper",
3
- "version": "3.11.0",
3
+ "version": "3.12.0",
4
4
  "description": "DSH Web 壁纸引擎 Wallpaper Engine mpkg 背景插件:浏览器内直接解析 .mpkg(preview.gif 动态背景/内嵌 mp4 视频/多时段切换),整屏统一虚化/对话框/弹层/遮罩独立虚化、镜头缩放平移、时钟、冲突检测。",
5
5
  "private": false,
6
6
  "type": "module",