dsh-folia 4.0.6 → 4.0.7
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/lib/boot/index.js +42 -6
- package/lib/routes/boot.js +3 -2
- package/package.json +1 -1
package/lib/boot/index.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* 3. **任何异常都必须放行**。注入的脚本顶层 try/catch,超时自毁,绝不把用户挡在门外。
|
|
10
10
|
*/
|
|
11
11
|
import { createHash } from 'node:crypto';
|
|
12
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
12
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
13
13
|
import { homedir } from 'node:os';
|
|
14
14
|
import { extname, join } from 'node:path';
|
|
15
15
|
|
|
@@ -96,8 +96,42 @@ export function clipMetaFile(id) {
|
|
|
96
96
|
if (!String(id || '')) return null;
|
|
97
97
|
return join(bootDir(), clipStem(id) + '.json');
|
|
98
98
|
}
|
|
99
|
+
/** 旧命名规则下的路径:`<id>.mp4`(id 当年被限制成 ASCII 安全字符,所以能直接用)。 */
|
|
100
|
+
function legacyClipFile(id) {
|
|
101
|
+
const raw = String(id || '');
|
|
102
|
+
if (!/^[a-z0-9][a-z0-9._-]*$/i.test(raw)) return null;
|
|
103
|
+
return join(bootDir(), raw + '.mp4');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* 找出这个 id 实际对应的片段文件。
|
|
108
|
+
*
|
|
109
|
+
* 为什么需要"找"而不是直接算:4.0.2 把磁盘命名从 `<id>.mp4` 改成了
|
|
110
|
+
* `<id>-<hash>.mp4`(为了支持中文 slug),于是**升级前下载的片段**还叫旧名字。
|
|
111
|
+
* 列表是按文件名枚举的,所以旧文件看得见;可点「使用」时按新名字找不到 ⇒ `not-installed`。
|
|
112
|
+
* 这里按"新名字 → 旧名字"的顺序找,找到旧名字就**顺手迁移**成新名字(幂等)。
|
|
113
|
+
*/
|
|
114
|
+
export function resolveClipFile(id) {
|
|
115
|
+
const next = clipFile(id);
|
|
116
|
+
if (next && existsSync(next) && statSync(next).size > 0) return next;
|
|
117
|
+
const legacy = legacyClipFile(id);
|
|
118
|
+
if (legacy && existsSync(legacy) && statSync(legacy).size > 0) {
|
|
119
|
+
try {
|
|
120
|
+
if (next && !existsSync(next)) writeFileSync(next, readFileSync(legacy));
|
|
121
|
+
try { renameSync(legacy, next); } catch { /* 跨盘/占用:复制已经够用,旧文件留着无害 */ }
|
|
122
|
+
try { rmSync(legacy); } catch { /* ignore */ }
|
|
123
|
+
const metaPath = clipMetaFile(id);
|
|
124
|
+
if (metaPath && !existsSync(metaPath)) {
|
|
125
|
+
writeFileSync(metaPath, JSON.stringify({ id, migratedFrom: 'legacy' }, null, 2) + '\n');
|
|
126
|
+
}
|
|
127
|
+
} catch { /* 迁移失败也不影响这次使用 */ }
|
|
128
|
+
return next || legacy;
|
|
129
|
+
}
|
|
130
|
+
return next;
|
|
131
|
+
}
|
|
132
|
+
|
|
99
133
|
export function clipExists(id) {
|
|
100
|
-
const p =
|
|
134
|
+
const p = resolveClipFile(id);
|
|
101
135
|
return Boolean(p && existsSync(p) && statSync(p).size > 0);
|
|
102
136
|
}
|
|
103
137
|
|
|
@@ -128,11 +162,13 @@ export function listLocalClips() {
|
|
|
128
162
|
}
|
|
129
163
|
|
|
130
164
|
export function removeClip(id) {
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
165
|
+
const paths = [clipFile(id), legacyClipFile(id)].filter(Boolean);
|
|
166
|
+
let removed = false;
|
|
167
|
+
for (const p of paths) {
|
|
168
|
+
try { if (existsSync(p)) { rmSync(p); removed = true; } } catch { /* 继续尝试下一个 */ }
|
|
169
|
+
}
|
|
134
170
|
try { const m = clipMetaFile(id); if (m && existsSync(m)) rmSync(m); } catch { /* ignore */ }
|
|
135
|
-
return true;
|
|
171
|
+
return removed || true;
|
|
136
172
|
}
|
|
137
173
|
|
|
138
174
|
/* ── 注入的启动屏幕脚本 ─────────────────────────────────────────────────────
|
package/lib/routes/boot.js
CHANGED
|
@@ -14,7 +14,7 @@ import { join } from 'node:path';
|
|
|
14
14
|
import { sendJson } from '../json-response.js';
|
|
15
15
|
import { bodyReader } from '../http-body.js';
|
|
16
16
|
import { resolveModules } from './modules.js';
|
|
17
|
-
import { clipExists, clipFile, clipMetaFile, listLocalClips, readBoot, removeClip, writeBoot } from '../boot/index.js';
|
|
17
|
+
import { clipExists, clipFile, clipMetaFile, listLocalClips, readBoot, removeClip, resolveClipFile, writeBoot } from '../boot/index.js';
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* 站点基址。开机动画的候选**从站点分类取**(`?category=boot-animation`),
|
|
@@ -397,7 +397,8 @@ export function registerBootRoutes(webServer, c) {
|
|
|
397
397
|
handler: async (req, res) => {
|
|
398
398
|
const boot = readBoot();
|
|
399
399
|
const id = boot.clipId;
|
|
400
|
-
|
|
400
|
+
// 旧命名的片段(升级前下载的)也要能播 —— 解析器会顺手迁移成新名字。
|
|
401
|
+
const file = resolveClipFile(id);
|
|
401
402
|
if (!file || !existsSync(file)) { res.statusCode = 404; res.end('no clip'); return; }
|
|
402
403
|
const size = statSync(file).size;
|
|
403
404
|
const range = String((req.headers && req.headers.range) || '');
|