Ethscriptions commited on
Commit
9e00275
·
verified ·
1 Parent(s): 908a5b6

E9: 以太坊简史 测试修复 + 使用说明(最终版)

Browse files
Files changed (3) hide show
  1. README.md +18 -1
  2. ethereum/js/chapters.js +4 -4
  3. ethereum/js/world.js +1 -1
README.md CHANGED
@@ -154,7 +154,7 @@ python shouhouzi/tools/subset_font.py <ZCOOLKuaiLe-Regular.ttf> # 重新做字
154
  | E6 | 音频:Worker 内纯 JS DSP(PolyBLEP 锯齿波、SVF 滤波、乒乓延迟)合成 13 段情绪配乐 + 60 余个画面同步音效;主线程卷积混响 + 人声链 + 压缩/限幅母带,整体约 -14 dBFS RMS | ✅ 完成 |
155
  | E7 | 播放器 + MP4 导出(Mediabunny:H.264/AAC 优先,自动回退 VP9/AV1 + Opus;可流式写盘)+ 24/30/60fps + 横竖屏,已实测导出并回放 | ✅ 完成 |
156
  | E8 | 横/竖封面 PNG + 抖音 / B站发布文案(含自动章节时间戳与数据说明) | ✅ 完成 |
157
- | E9 | 自动化测试、逐镜检查、修复、最终推送 | ⏳ |
158
 
159
  ## E1. 风格定义:「深空 · 光之晶体」
160
 
@@ -191,3 +191,20 @@ python shouhouzi/tools/subset_font.py <ZCOOLKuaiLe-Regular.ttf> # 重新做字
191
  | 音频 | Web Worker 里用纯 JS DSP 逐采样合成配乐(合成器 pad / 琶音 / 低音 / 鼓)与音效(冲击、上升音、故障音、心跳……),主线程再用卷积混响 + 压缩 + 限幅做母带,与旁白混音 |
192
  | 导出 | Mediabunny(MP4 封装 + WebCodecs 编码):自动选择 H.264/AAC,不支持时回退;支持直接流式写入磁盘 |
193
  | 字幕 | 逐词点亮的卡拉 OK 式字幕 |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  | E6 | 音频:Worker 内纯 JS DSP(PolyBLEP 锯齿波、SVF 滤波、乒乓延迟)合成 13 段情绪配乐 + 60 余个画面同步音效;主线程卷积混响 + 人声链 + 压缩/限幅母带,整体约 -14 dBFS RMS | ✅ 完成 |
155
  | E7 | 播放器 + MP4 导出(Mediabunny:H.264/AAC 优先,自动回退 VP9/AV1 + Opus;可流式写盘)+ 24/30/60fps + 横竖屏,已实测导出并回放 | ✅ 完成 |
156
  | E8 | 横/竖封面 PNG + 抖音 / B站发布文案(含自动章节时间戳与数据说明) | ✅ 完成 |
157
+ | E9 | 自动化测试(横竖屏 63 句逐句截图 ×2、音轨电平、两种画幅/帧率导出并解码回放)、修复构图与字体回退、最终推送 | ✅ 完成 |
158
 
159
  ## E1. 风格定义:「深空 · 光之晶体」
160
 
 
191
  | 音频 | Web Worker 里用纯 JS DSP 逐采样合成配乐(合成器 pad / 琶音 / 低音 / 鼓)与音效(冲击、上升音、故障音、心跳……),主线程再用卷积混响 + 压缩 + 限幅做母带,与旁白混音 |
192
  | 导出 | Mediabunny(MP4 封装 + WebCodecs 编码):自动选择 H.264/AAC,不支持时回退;支持直接流式写入磁盘 |
193
  | 字幕 | 逐词点亮的卡拉 OK 式字幕 |
194
+
195
+ ## E4. 使用说明(以太坊简史)
196
+
197
+ - **在线观看**:https://ethscriptions-pols.static.hf.space/ethereum/index.html (首页导航:https://ethscriptions-pols.static.hf.space/ )
198
+ - **时长**:6 分 54 秒,共 13 章、63 句旁白,画面同步点均由逐词时间戳自动换算。
199
+ - **播放**:空格播放/暂停,←/→ 快退/快进 5 秒,M 静音,F 全屏;章节卡片可以直接跳转;在进度条上悬停会显示时间和章节。
200
+ - **画幅**:横屏 16:9(B站)/ 竖屏 9:16(抖音)。竖屏单独设计了构图,不是简单裁切;打开「手机安全区参考线」可以查看抖音的遮挡区域。
201
+ - **导出**:选画幅 / 帧率(24·30·60)/ 码率 / 范围 → 开始导出。桌面版 Chrome / Edge 输出 H.264 + AAC;浏览器不支持时自动回退。支持「边渲染边写入磁盘」。
202
+ - **封面**:`ethereum/covers/cover-landscape.png`、`cover-portrait.png`,网页里也可以实时重绘下载。
203
+ - **发布文案**:抖音 / B站标题、简介(含章节时间戳与数据说明)、标签、分区建议,点一下就能复制。
204
+
205
+ ### 重新构建
206
+ ```bash
207
+ pip install edge-tts mutagen fonttools brotli
208
+ python ethereum/tools/build_voice.py # 改文案(含 {标记})后重新配音与对齐
209
+ python ethereum/tools/fonts.py <字体源目录> # 重新子集化三款字体
210
+ ```
ethereum/js/chapters.js CHANGED
@@ -547,9 +547,9 @@
547
  if (offK < 0.9) K.glow(ctx, bx, by, 90, C.orange, 0.3 * a * (1 - offK));
548
  }
549
  };
550
- if (pw > 0) { const [x, y] = H.bx(0.22, 0.55, 0.27, 0.86); ctx.save(); if (P) { ctx.translate(x, y); ctx.scale(0.62, 0.62); ctx.translate(-x, -y); } rigs(x, y, pw, 0); K.text(ctx, '工作量证明 · PoW', x, y + 130, { size: 34, color: C.orange, alpha: pw }); K.text(ctx, '矿机算力竞争记账', x, y + 176, { size: 26, color: K.rgba(C.white, 0.7), alpha: pw }); ctx.restore(); }
551
  if (ps > 0) {
552
- const [x, y] = H.bx(0.78, 0.55, 0.75, 0.86);
553
  ctx.save(); if (P) { ctx.translate(x, y); ctx.scale(0.62, 0.62); ctx.translate(-x, -y); } ctx.globalAlpha *= ps;
554
  for (let i = 0; i < 5; i++) { const a = i / 5 * K.TAU + t * 0.4; const sx = x + Math.cos(a) * 110, sy = y + Math.sin(a) * 60; K.glow(ctx, sx, sy, 50, C.gold, 0.6); const sh = [[0, -26], [22, -16], [18, 12], [0, 28], [-18, 12], [-22, -16], [0, -26]].map(([px, py]) => [sx + px, sy + py]); K.neon(ctx, sh, C.gold, 2, 1); }
555
  ctx.restore();
@@ -588,7 +588,7 @@
588
  // 矿机熄灭 + 能耗
589
  const mk = H.lf('mine', null, 0.6);
590
  if (mk > 0) {
591
- const [x, y] = H.bx(0.18, 0.6, 0.27, 0.86);
592
  ctx.save(); if (P) { ctx.translate(x, y); ctx.scale(0.62, 0.62); ctx.translate(-x, -y); } ctx.globalAlpha *= 1 - K.clamp(H.m('energy') / 1); rigs(x, y, mk, off); ctx.restore();
593
  const ek2 = H.lf('energy', null, 0.6);
594
  if (ek2 > 0) {
@@ -779,7 +779,7 @@
779
  icons.forEach(([m, a, b], i) => {
780
  const k = K.life(t, H.at(m), H.at('bye'), 0.6, 0.6);
781
  if (k <= 0) return;
782
- const x = P ? L.box.x + L.box.w * (0.14 + i * 0.24) : c.x + (i - 1.5) * 300, y = c.y + c.s * (P ? 2.9 : 2.1);
783
  pop(ctx, x, y, k, () => { K.glass(ctx, -120, -60, 240, 120, { tint: [C.red, C.etc, C.mag, C.gold][i] }); K.text(ctx, a, 0, -14, { size: 32, color: C.white }); K.text(ctx, b, 0, 28, { size: 24, fam: 'num', color: [C.red, '#7CE07C', C.mag, C.gold][i] }); });
784
  });
785
  const ok = H.lf('open', 'bye', 0.9, 0.6);
 
547
  if (offK < 0.9) K.glow(ctx, bx, by, 90, C.orange, 0.3 * a * (1 - offK));
548
  }
549
  };
550
+ if (pw > 0) { const [x, y] = H.bx(0.22, 0.55, 0.27, 0.8); ctx.save(); if (P) { ctx.translate(x, y); ctx.scale(0.62, 0.62); ctx.translate(-x, -y); } rigs(x, y, pw, 0); K.text(ctx, '工作量证明 · PoW', x, y + 130, { size: 34, color: C.orange, alpha: pw }); K.text(ctx, '矿机算力竞争记账', x, y + 176, { size: 26, color: K.rgba(C.white, 0.7), alpha: pw }); ctx.restore(); }
551
  if (ps > 0) {
552
+ const [x, y] = H.bx(0.78, 0.55, 0.75, 0.8);
553
  ctx.save(); if (P) { ctx.translate(x, y); ctx.scale(0.62, 0.62); ctx.translate(-x, -y); } ctx.globalAlpha *= ps;
554
  for (let i = 0; i < 5; i++) { const a = i / 5 * K.TAU + t * 0.4; const sx = x + Math.cos(a) * 110, sy = y + Math.sin(a) * 60; K.glow(ctx, sx, sy, 50, C.gold, 0.6); const sh = [[0, -26], [22, -16], [18, 12], [0, 28], [-18, 12], [-22, -16], [0, -26]].map(([px, py]) => [sx + px, sy + py]); K.neon(ctx, sh, C.gold, 2, 1); }
555
  ctx.restore();
 
588
  // 矿机熄灭 + 能耗
589
  const mk = H.lf('mine', null, 0.6);
590
  if (mk > 0) {
591
+ const [x, y] = H.bx(0.18, 0.6, 0.27, 0.8);
592
  ctx.save(); if (P) { ctx.translate(x, y); ctx.scale(0.62, 0.62); ctx.translate(-x, -y); } ctx.globalAlpha *= 1 - K.clamp(H.m('energy') / 1); rigs(x, y, mk, off); ctx.restore();
593
  const ek2 = H.lf('energy', null, 0.6);
594
  if (ek2 > 0) {
 
779
  icons.forEach(([m, a, b], i) => {
780
  const k = K.life(t, H.at(m), H.at('bye'), 0.6, 0.6);
781
  if (k <= 0) return;
782
+ const x = P ? L.box.x + L.box.w * (0.14 + i * 0.24) : c.x + (i - 1.5) * 300, y = c.y + c.s * (P ? 2.2 : 2.1);
783
  pop(ctx, x, y, k, () => { K.glass(ctx, -120, -60, 240, 120, { tint: [C.red, C.etc, C.mag, C.gold][i] }); K.text(ctx, a, 0, -14, { size: 32, color: C.white }); K.text(ctx, b, 0, 28, { size: 24, fam: 'num', color: [C.red, '#7CE07C', C.mag, C.gold][i] }); });
784
  });
785
  const ok = H.lf('open', 'bye', 0.9, 0.6);
ethereum/js/world.js CHANGED
@@ -147,7 +147,7 @@
147
 
148
  // 晶体在各章节的"站位"(相对内容框,0..1)。章节交界处平滑过渡。
149
  const POSE = {
150
- open: { l: [0.5, 0.42, 0.3], p: [0.5, 0.4, 0.24] },
151
  origin: { l: [0.86, 0.22, 0.09], p: [0.83, 0.07, 0.07] },
152
  ico: { l: [0.14, 0.2, 0.08], p: [0.15, 0.06, 0.07] },
153
  genesis: { l: [0.5, 0.13, 0.07], p: [0.5, 0.05, 0.07] },
 
147
 
148
  // 晶体在各章节的"站位"(相对内容框,0..1)。章节交界处平滑过渡。
149
  const POSE = {
150
+ open: { l: [0.5, 0.39, 0.25], p: [0.5, 0.36, 0.18] },
151
  origin: { l: [0.86, 0.22, 0.09], p: [0.83, 0.07, 0.07] },
152
  ico: { l: [0.14, 0.2, 0.08], p: [0.15, 0.06, 0.07] },
153
  genesis: { l: [0.5, 0.13, 0.07], p: [0.5, 0.05, 0.07] },