Files
phptits 851ccf1d7f v2 platform runtime (docs 22 Phase 2/4/7 + 17): Project/Environment + Config + Storage + Observability
- Project/Environment model with default environments (dev/staging/prod)
- Config Runtime: cascade resolution (platform→org→project→env), secrets via vault
- Storage Runtime: local provider, sha256 dedup, integrity check, file list/upload/delete
- Observability: execution query, per-action summary (success rate, avg, p95), health
- Realtime SSE: workspace event stream endpoint (GET /api/v1/v2/realtime/events)
- Phase 1 acceptance: core.health.check, core.user.create, core.user.list
- Actions: core.project.*, core.config.*, core.file.*, core.user.* (62 total)
2026-09-11 21:11:07 +00:00

10 KiB
Raw Permalink Blame History

动态 FPS 截图推送模块

1. 目标

现有 Browser SSE 已经支持 Screenshot 推送,本次只增加动态 Screenshot 推送频率。

核心规则:

  • 无操作时:每 2 秒 推送一张 Screenshot
  • 用户发生浏览器操作后:进入高频模式
  • 高频模式:每 500ms 推送一张 Screenshot
  • 高频模式持续 5 秒
  • 5 秒的计算基于最后一次浏览器操作
  • 新操作发生时,只延长高频模式时间
  • 操作本身不立即触发 Screenshot
  • 不使用真正的视频流
  • 不使用 WebRTC
  • 不使用 VNC
  • Screenshot 仍然是独立 JPEG 图片
  • Screenshot 仍然通过现有 SSE 推送

2. 配置

默认配置:

const screenshotConfig = {
  quality: 30,

  normalInterval: 2000,

  activeInterval: 500,

  activeDuration: 5000
}

参数:

参数 默认值 说明
quality 30 JPEG 质量
normalInterval 2000 普通模式截图间隔,单位 ms
activeInterval 500 活跃模式截图间隔,单位 ms
activeDuration 5000 最后一次操作后保持活跃模式的时间,单位 ms

所有时间参数统一使用 ms。


3. Screenshot Mode

只有两种状态:

type ScreenshotMode = 'normal' | 'active'

Normal

interval = 2000ms

即:

Screenshot
    ↓
等待 2 秒
    ↓
Screenshot
    ↓
等待 2 秒
    ↓
...

Active

interval = 500ms

即:

Screenshot
    ↓
等待 500ms
    ↓
Screenshot
    ↓
等待 500ms
    ↓
...

4. 状态

Scheduler 至少维护:

interface ScreenshotSchedulerState {
  mode: 'normal' | 'active'

  lastActivityAt: number

  activeUntil: number

  running: boolean
}

说明:

mode

当前截图模式:

normal
active

lastActivityAt

最后一次成功 Browser Action 的时间。

activeUntil

Active Mode 的结束时间。

例如:

当前时间:10:00:00

用户操作
    ↓
activeUntil = 10:00:05

5. 操作触发 Active Mode

任何成功的浏览器操作完成后调用:

notifyActivity()

实现:

function notifyActivity() {
  const now = Date.now()

  lastActivityAt = now
  activeUntil = now + activeDuration
}

同时:

mode = 'active'

6. 操作不立即 Screenshot

这是必须遵守的规则。

错误:

Browser Action
      ↓
Screenshot
      ↓
SSE

正确:

Browser Action
      ↓
Action 执行成功
      ↓
notifyActivity()
      ↓
切换 Active Mode
      ↓
Scheduler 按 500ms 推送 Screenshot

也就是说:

Browser Action 只修改 Screenshot Scheduler 状态,不主动调用 Screenshot。


7. Active Mode 生命周期

例如:

00:00
用户 click
    ↓
Active Until = 00:05

00:00.5 Screenshot
00:01.0 Screenshot
00:01.5 Screenshot
00:02.0 Screenshot
...
00:05.0
    ↓
恢复 Normal

恢复后:

每 2000ms Screenshot

8. 连续操作

如果 Active Mode 中再次发生操作:

00:00 click
    ↓
activeUntil = 00:05

00:02 fill
    ↓
activeUntil = 00:07

00:04 scroll
    ↓
activeUntil = 00:09

因此:

Active Mode 的 5 秒永远从最后一次操作重新计算。

最终:

00:04 最后一次操作
      ↓
保持 Active 到 00:09
      ↓
恢复 Normal

9. 多次操作不会创建多个 Timer

禁止:

setInterval(...)
setInterval(...)
setInterval(...)

也禁止每次操作创建一个新的 Screenshot Loop。

整个 Browser Session 只允许存在:

1 个 Screenshot Scheduler

所有操作统一调用:

notifyActivity()

10. Scheduler

推荐采用单一循环。

示例:

class ScreenshotScheduler {
  private running = false

  private mode: 'normal' | 'active' = 'normal'

  private activeUntil = 0

  async start() {
    if (this.running) {
      return
    }

    this.running = true

    while (this.running) {
      const now = Date.now()

      this.mode =
        now < this.activeUntil
          ? 'active'
          : 'normal'

      await this.capture()

      const interval =
        this.mode === 'active'
          ? this.config.activeInterval
          : this.config.normalInterval

      await sleep(interval)
    }
  }

  notifyActivity() {
    const now = Date.now()

    this.mode = 'active'

    this.activeUntil =
      now + this.config.activeDuration
  }

  stop() {
    this.running = false
  }
}

实际代码可以根据项目结构调整,但必须保持相同的行为。


11. Screenshot 执行顺序

Scheduler 每次执行:

检查当前 Mode
      ↓
Screenshot
      ↓
SSE 推送
      ↓
根据当前 Mode 等待
      ↓
进入下一轮

例如 Normal:

Screenshot
↓
wait 2000ms
↓
Screenshot
↓
wait 2000ms

Active:

Screenshot
↓
wait 500ms
↓
Screenshot
↓
wait 500ms

12. Screenshot 不允许并发

必须保证同一 Browser Session:

同时最多 1 个 Screenshot

禁止:

Screenshot A
    ↓
还没完成
Screenshot B
    ↓
还没完成
Screenshot C

正确:

Screenshot A
    ↓
完成
    ↓
等待 interval
    ↓
Screenshot B

原因:

  • 避免 CPU 突增
  • 避免内存占用增加
  • 避免多个 Screenshot 同时进入 SSE
  • 避免页面状态竞争

13. Browser Action 与 Scheduler 的关系

Browser Controller 在 Action 成功后:

await executeAction(action)

scheduler.notifyActivity()

不要:

await executeAction(action)

await screenshot()

也不要:

scheduler.start()

Browser Session 创建时只启动一次 Scheduler。


14. Action 失败

Action 失败:

Browser Action
      ↓
失败
      ↓
不调用 notifyActivity()

因此不会进入 Active Mode。

例如:

click element
    ↓
Element Not Found
    ↓
Action Failed
    ↓
保持 Normal Mode

15. Navigation Action

以下操作成功后同样进入 Active Mode:

goto
reload
back
forward

例如:

goto
 ↓
页面加载完成
 ↓
notifyActivity()
 ↓
Active Mode
 ↓
500ms Screenshot

16. Batch Action

Batch Action 只调用一次:

scheduler.notifyActivity()

例如:

Action 1
 ↓
Action 2
 ↓
Action 3
 ↓
全部成功
 ↓
notifyActivity()

不要:

Action 1 → notifyActivity()
Action 2 → notifyActivity()
Action 3 → notifyActivity()

Batch 本身只需要最终刷新一次 Active Mode 时间。


17. SSE

动态 FPS 不改变现有 SSE API。

仍然:

GET /profiles/:profileId/browser/events

Screenshot Event 保持现有格式。

Scheduler 只决定:

什么时候生成下一张 Screenshot。

SSE 只负责:

把 Screenshot 推送给前端。

两者职责保持分离。


18. SSE State

建议在 state event 中增加:

{
  "mode": "active",
  "normalInterval": 2000,
  "activeInterval": 500,
  "activeUntil": 1780000000000
}

前端可以知道当前 Screenshot Scheduler 状态。

如果现有 state 已经定义完成,也可以不修改协议,仅在服务端内部维护状态。


19. SSE Client 数量

如果当前没有 SSE Client:

SSE Client = 0

可以暂停 Screenshot Scheduler。

重新建立 SSE:

SSE Connected
    ↓
启动 Screenshot Scheduler
    ↓
发送当前 Screenshot
    ↓
进入 Normal Mode

如果当前项目已经有 Scheduler 生命周期管理,则只增加动态 interval,不修改现有连接管理。


20. Active Mode 不等于视频流

必须保持:

Active Mode
    ↓
每 500ms
    ↓
生成一张 JPEG

而不是:

Active Mode
    ↓
视频编码
    ↓
持续视频流

所以本功能仍然是:

Adaptive Screenshot Streaming

而不是 Live View / Video Streaming。


21. 推荐默认参数

第一版固定默认:

{
  quality: 30,
  normalInterval: 2000,
  activeInterval: 500,
  activeDuration: 5000
}

即:

无操作:
2 秒 / 张

操作后:
500ms / 张

最后一次操作后:
5 秒恢复正常

JPEG:
quality 30

后续如果需要,再将这些参数开放为系统配置。


22. 核心流程

                 Browser Action
                       │
                       ▼
                 Playwright
                       │
                 操作成功
                       │
                       ▼
                notifyActivity()
                       │
                       ▼
                 Active Mode
                 interval=500ms
                       │
                       ▼
                  Screenshot
                       │
                       ▼
                      SSE
                       │
                       ▼
                    Console
                       │
                       │
                  500ms 后
                       │
                       ▼
                  Screenshot
                       │
                      ...
                       │
              最后操作 + 5 秒
                       │
                       ▼
                 Normal Mode
                 interval=2000ms
                       │
                       ▼
                  Screenshot
                       │
                    2 秒后
                       │
                       ▼
                  Screenshot

23. 验收标准

  • Normal Mode 默认 2000ms
  • Active Mode 默认 500ms
  • Active Duration 默认 5000ms
  • Browser Action 成功后进入 Active Mode
  • Browser Action 不立即触发 Screenshot
  • Action 失败不进入 Active Mode
  • Active Mode 从最后一次操作重新计时
  • 连续操作不会创建多个 Timer
  • 每个 Browser Session 只有一个 Screenshot Scheduler
  • Screenshot 不允许并发执行
  • Active Mode 结束后自动恢复 Normal Mode
  • SSE 接口保持不变
  • Screenshot 仍然使用 JPEG
  • Screenshot quality 默认 30
  • 不引入 WebSocket
  • 不引入 WebRTC
  • 不引入 VNC
  • 不引入 CDP
  • 不实现视频流