强化学习
> 从技能市场安装? 市场展示名为「强化学习」,技术标识(目录名)为 video-2x-autoplay。
> 中文触发词写在 description 里,目录名不同不影响调用。手动解压安装的步骤见
> references/install.md;市场安装可跳过其第 1–3 步。发布与审核事项见 references/publish.md。
用一条独立于用户日常浏览器的可见 Edge 通道,把培训平台的课程视频按目录顺序连续以平台原生 2 倍速播完。核心不是「阻止暂停」,而是控制通道永不抢焦点:所有交互都是页面内 JS,真实鼠标、键盘、窗口激活、标签切换一概不用;失焦造成的偶发暂停用一次正常 play() 兜底恢复。
> 名称澄清:本 skill 与机器学习中的「强化学习」算法无关,仅为用户指定的技能名。
何时使用 / 何时不用
- ✅ 使用:本人在平台已登录;平台允许倍速;目录里是一串可点的视频节;需要在播放期间继续用电脑做别的事。
- ❌ 不使用:需要代答测验、代交卷、代做人脸核验;需要 headless/后台隐藏窗口;平台明确禁止倍速或禁止自动化辅助;需要伪造学时。
- ⚠️ 完成度 ≠ 学时。服务端计时才是权威,脚本只保证「视频真实以 2x 播完」,不保证学时到账。
硬约束(不可放宽,来自已确认的边界)
- 不抢焦点:只用
page.evaluate页面内 JS。不移动真实鼠标、不按键、不切标签、不bring_to_front、不激活窗口、不page.mouse.*。 - 不伪造状态:不覆盖
document.hidden/visibilityState、不派发visibilitychange/blur/mouseleave、不合成鼠标事件、不覆盖平台方法。 - 不伪造进度:不 seek、不 POST 进度/心跳接口。倍速优先点平台自己的控件,只有控件缺失才降级改
playbackRate,并读回校验。 - 人工环节绝不代劳:测验、防挂机弹窗、人脸核验、异常学习提示 → 只截图、只提示、写
need_human.json后等待本人处理;脚本不点选项、不提交、不代答。目录里的「单元测验」类节点列入pending_human.json,不进播放队列。 - 有界失败:恢复次数、等待时长、重试轮数都有硬上限;异常一律停机汇报,不做无限重试。
- 可见窗口:必须非 headless。用户可把该窗口拖到角落/副屏、可被其他窗口盖住,但不要最小化它,也不要在该窗口里切到别的标签页——这两种操作会让
visibilityState变 hidden,脚本会停机。
环境准备(先做一次)
- 依赖:
python -m pip install playwright。本 skill 用connect_over_cdp连接已启动的 Edge,不需要playwright install(不下载浏览器二进制)。 - 解释器自检:
python -c "import playwright; print('ok')";报ModuleNotFoundError时执行python -m pip install playwright。下文$PY一律指目标电脑上装了 Playwright 的那个 Python(Windows 常见py -3或某个 venv 里的python.exe),不要照抄其他机器的绝对路径。 - 平台限制:
browser.py用netstat+taskkill /F /T反查并清理浏览器进程,目前仅支持 Windows + Edge/Chromium。安装到另一台电脑、以及非 Windows 的处置见references/install.md。 - Edge 路径自动探测;找不到时用
browser.py --edge "<msedge.exe 完整路径>"指定。 <SKILL_DIR>= 本SKILL.md所在目录;<WORK>= 运行目录,所有运行时产物(profile、日志、状态、截图)都写这里,默认./rl_run。给每个任务换一个--port,避免连到别人的浏览器会话。- 可选自检(不登录平台、不碰现有浏览器):
$PY "<SKILL_DIR>/scripts/selftest.py"。它用临时 headless Edge 构造 mock 目录与播放器,逐项验证内嵌 JS 与适配器,输出SELFTEST_OK即通过。自检里method=platform-ui+dom-fallback是正常的(mock 的倍速按钮没绑定事件);真实平台应显示method=platform-ui。
标准流程(6 步)
第 1 步|准备课程 URL 列表 <WORK>/courses.txt,每行一个完整课程页 URL,允许空行和 # 注释:
https://peixun.amac.org.cn/learn/XXXX?studentCourseId=YYY
https://peixun.amac.org.cn/learn/ZZZZ?studentCourseId=WWW
第 2 步|启动专用可见浏览器(长驻,后台运行)
$PY "<SKILL_DIR>/scripts/browser.py" hold --port 9338 --work-dir "<WORK>" \
--url "https://peixun.amac.org.cn/study"
hold 会启动一个独立 profile 的可见 Edge 并常驻持有它。必须用后台任务方式运行(该进程在播放期间不能退出)。启动后脚本打印 KEEPALIVE_READY。宿主环境会在短命令结束时回收子进程树,所以不能把浏览器交给一次性脚本启动。
第 3 步|本人在弹出的专用 Edge 窗口登录一次。登录态保存在独立 profile,后续复用;不要关掉这个窗口。
第 4 步|只读探测并生成播放清单
$PY "<SKILL_DIR>/scripts/probe.py" --port 9338 --work-dir "<WORK>" \
--adapter "<SKILL_DIR>/scripts/adapters/amac.json" --urls-file "<WORK>/courses.txt"
成功打印每门课的视频数/测验数/@2x 预计墙钟时间,并写 <WORK>/playlist.json。新平台先用 --inspect 看 DOM 候选,再按 references/adapter-guide.md 写自己的适配器。
第 5 步|小范围验证 → 全量播放
# 先只播 1 节,确认倍速与完成判定正常
$PY "<SKILL_DIR>/scripts/play.py" --port 9338 --work-dir "<WORK>" \
--adapter "<SKILL_DIR>/scripts/adapters/amac.json" --limit 1
# 验证通过后全量(可反复执行,--resume 按 state.json 与清单中的完成标记跳过已播节;
# 若需按服务端最新完成状态重建清单,先重跑第 4 步 probe.py)
$PY "<SKILL_DIR>/scripts/play.py" --port 9338 --work-dir "<WORK>" \
--adapter "<SKILL_DIR>/scripts/adapters/amac.json" --resume
长任务用后台方式运行,并轮询查看输出、主动汇报进度(禁止静默等待)。运行中每 12 秒写一次 <WORK>/heartbeat.json(时间戳/当前节/播放位置);超过 ~60 秒未更新说明进程已被宿主回收,需重新执行本步(不必重新登录)。
第 6 步|复核 + 清理
$PY "<SKILL_DIR>/scripts/verify.py" --port 9338 --work-dir "<WORK>" \
--adapter "<SKILL_DIR>/scripts/adapters/amac.json" # 只读,播完后才运行
$PY "<SKILL_DIR>/scripts/browser.py" cleanup --port 9338 --work-dir "<WORK>"
cleanup 按 CDP 端口反查真实主进程 PID,再 taskkill /F /T /PID 杀整棵进程树;严禁 taskkill /IM msedge.exe(会误杀用户自己的浏览器)。也可向 <WORK>/stop_<port>.flag 写入 1,让 hold 自行收尾。
运行中用户该做什么
- 正常用其他窗口、其他应用、甚至把专用窗口盖在下面 → 可以,实测 695 次
focus_absent中只有 27 次真正需要一次play()恢复,visibility_hidden为 0。 - 看到专用窗口弹出测验/核验 → 本人点选、提交;脚本检测到弹窗消失后自动继续播放。答案可能需要自行分析,脚本只提供截图。
- 平台提示「已被迫下线」→ 在专用窗口重新登录,脚本以退出码 5 停下,登录后重跑第 5 步(
--resume)即可。 - 不要最小化专用窗口、不要在该窗口切换标签、不要关掉它。
退出码与运行时文件
| 码 | 含义 | 处置 |
|---|---|---|
| 0 | 全部播完 | 运行 verify.py 复核 |
| 2 | 未找到可连接的页面/未登录 | 检查 hold 与登录态 |
| 3 | 一般故障(倍速未生效、卡住、超时、暂停未解决) | 看 play.log 与 need_human.json |
| 4 | 人工处理超时(默认 15 分钟) | 本人处理弹窗后 --resume 重跑 |
| 5 | 会话失效 | 重新登录后 --resume 重跑 |
<WORK> 内:playlist.json、state.json、play.log、heartbeat.json、need_human.json(人工待办)、pending_human.json(单元测验清单)、gate_*.png(弹窗截图)、verify.json、edge_profile_<port>/(登录态)。
关键实现要点(改代码前必读)
- 完成判定必须放在暂停检查之前:不少平台片尾会把播放器重置为
ct=0 / paused=true,当成「暂停」会误停机。以服务端.learnDone/doneCount/已学 N%为准。 - 失焦只记 WARN;仅当
paused=true && hasFocus=false && 无弹窗时调一次正常play(),并有 20 次硬上限。 - 目录去重必须按资源 id(
data-jhx-res一类),不能按标题文本;同章节下两个视频可能同名。 visibilityState != "visible"直接停机,不重试、不恢复。- 跨课程导航用
page.goto(GET),不新开标签;脚本结束不调用browser.close(),浏览器由hold持有。
参考资源
scripts/adapters/amac.json:中基协远程培训平台实测适配器(26 个视频、2x 全部播完)。references/install.md:在另一台电脑安装/验证/首次使用的完整步骤与平台限制。references/adapter-guide.md:适配器字段表 + 新平台适配 5 步。references/pitfalls.md:实测证据、进程/编码坑、已知限制、合规边界、排错速查。references/publish.md:技能市场发布流程、必改字段、规范依据与审核风险(发布者读)。
合规边界
仅用于本人账号在平台允许倍速的前提下做真实播放辅助;不绕过异常学习检测、不伪造学时、不代答代交。若平台规则禁止自动化辅助,应停止使用并改为本人手动观看。