Files
2026-03-26 10:33:45 -05:00

383 lines
9.9 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 云手机课程章节详情配置(克隆自 nomadlms
*/
const SAMPLE_VIDEO = "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4";
const LESSON_MARKDOWNS: Record<string, string> = {
"0-0": `# 01 从0到1安卓镜像安装与首次开机
对应电子书章节:\`硬件选择\` -> \`刷入安卓系统\`
## 你会完成什么
- 准备 TF 卡、读卡器、开发板电源与网线
- 下载开发板安卓镜像AOSP
- 用 Balena Etcher 刷写 img 并首次开机
## 实操步骤
1. 将 img 镜像写入 TF 卡
2. TF 卡插回开发板,接通电源和网线
3. 首次启动后确认系统已进入桌面并可调试
## 避坑提醒
- 刷机前优先确认镜像版本与板型匹配
- 首次开机时间可能较长,不要中途断电
`,
"0-1": `# 02 性能翻倍:安卓存储扩容实操
对应电子书章节:\`硬件选择\` 与存储相关说明TF/SSD 组合)。
## 核心目标
- 解决默认系统空间不足导致的安装失败
- 保证后续安装模块、素材、脚本有足够空间
## 推荐方案
- TF 卡至少 64GB
- 优先使用 USB3.0 读卡器提升刷写效率
- 有条件时搭配 SSD 作为素材盘
## 执行清单
- [ ] 检查系统可用容量
- [ ] 清理无用预装与缓存
- [ ] 预留直播素材和日志空间
`,
"1-0": `# 03 竖屏直播必备:系统显示方向设置
对应电子书章节:\`业务场景:无人直播\` 中摄像头与画面链路实践。
## 为什么先做竖屏
- TikTok 直播以竖屏为主
- 先固定方向,后续采集与推流才稳定
## 操作要点
1. 在系统中关闭自动旋转
2. 固定竖屏分辨率
3. 使用测试素材验证采集画面方向
## 验证标准
- 开播预览无横竖屏抖动
- 推流端与手机端画面方向一致
`,
"1-1": `# 04 一次搞定 RootMagisk + 模块安装
对应电子书章节:\`获取root权限\`\`安装lsposed环境\`
## 实操流程
1. 从镜像提取 \`boot.img\`
2. 在安卓端用 Magisk 修补镜像
3. 通过刷写工具或 adb dd 回刷补丁镜像
4. 重启验证 Root 生效
## 关键命令(电子书同源)
\`\`\`bash
adb shell
su
dd if=/sdcard/magisk_patched.img of=/dev/block/mmcblk0p9 bs=4M
sync
reboot
\`\`\`
## 后续动作
- 安装 LSPosed准备模块化能力
- 记录可回滚镜像,避免系统损坏
`,
"1-2": `# 05 防风控第一步:机型参数改造
对应电子书章节:\`修改机型\`
## 目标
- 降低平台对异常设备指纹的识别概率
- 让账号环境更贴近真实移动设备
## 建议流程
1. 安装改机工具并授予 Root 权限
2. 进入设备信息伪装,选择目标机型
3. 重启系统并重新检查参数是否生效
## 风险提示
- 机型参数、IP、GPS 需要协同一致
- 不建议频繁切换机型,避免触发异常行为
`,
"2-0": `# 06 降低风控命中GPS 精准定位
对应电子书章节:\`GPS定位\`
## 为什么必须做
- GPS 与 IP 国家/城市不一致,容易触发风控
- 直播账号稳定性与定位一致性强相关
## 操作步骤
1. 安装定位工具并配置 Mock Location
2. 通过地图搜索目标区域并手动校准
3. 启动模拟定位后再打开直播 App
## 检查清单
- [ ] GPS 城市与代理 IP 所在地区一致
- [ ] 时间时区与账号地区一致
- [ ] 开播前再次确认定位未漂移
`,
"2-1": `# 07 无人直播入门:录播素材循环推流
对应电子书章节:\`业务场景:无人直播\`\`录播mp4素材\`
## 核心链路
1. Linux 辅助板播放素材
2. HDMI 输出到采集卡
3. 采集卡通过 USB 输入安卓板,识别为摄像头
## 示例命令
\`\`\`bash
ffplay -loop 0 -vf "transpose=2,scale=640:-1,format=yuv420p" -fs -threads 6 xxx.mp4
\`\`\`
## 实战建议
- 先用短素材跑通链路,再换长素材
- 分辨率和码率不要一步拉满,先求稳定
`,
"2-2": `# 08 TikTok 实战开播:完整流程演示
对应电子书章节:\`业务场景:无人直播\`\`辅助板linux配置\`
## 开播前准备
- 辅助板已安装视频/音频驱动与 ffmpeg
- 安卓端已完成 Root、改机、定位
- 直播账号完成基础养号与资料设置
## 标准流程
1. 启动素材源或直播流
2. 在 TikTok 端预览并确认摄像头输入
3. 开播后观察前 10 分钟数据与风控提示
## 观察重点
- 是否弹验证窗口
- 是否出现明显限流
- 进房、停留、互动是否正常
`,
"2-3": `# 09 TikTok 进阶优化:稳定推流与细节调优
对应电子书章节:\`obs推流/srs拉流\`
## 进阶目标
- 降低延迟与卡顿
- 提升长时段推流稳定性
## SRS 部署思路
- 在辅助板部署 SRS
- OBS 走 SRT 推流,辅助板本地拉流喂给安卓摄像头
## 拉流脚本示例
\`\`\`bash
ffplay -fflags nobuffer -flags low_delay -framedrop \
-probesize 32 -analyzeduration 0 -sync ext -infbuf \
-vf "setpts=PTS-STARTPTS" \
'srt://127.0.0.1:10080?streamid=#!::r=live/livestream,m=request&latency=10'
\`\`\`
`,
"3-0": `# 10 软路由与科学上网
对应电子书章节:\`魔法上网\`
## 为什么是软路由优先
- 电子书实测:代理 APK 在部分场景难过风控
- 软路由链路更稳定,适合长期业务运行
## 推荐组合
- 海外 VPS中转
- 静态住宅 IP落地
- Clash Meta链式代理
## 操作顺序
1. 购买 VPS 与域名
2. 购买静态住宅 IP
3. 配置链式代理与策略路由
`,
"3-1": `# 11 静态住宅 IP 配置
对应电子书章节:\`静态住宅ip\`\`链式代理\`
## 核心原则
- 便宜节点通常不能国内直连,必须走中转
- IP、GPS、机型要形成一致的“环境画像”
## 配置要点
- 落地节点使用静态住宅 IP
- 中转与落地分层,避免单点故障
- 业务账号固定绑定线路,不频繁切换
## 验证指标
- DNS、出口国家、时区一致
- 连续开播无大规模验证弹窗
`,
"4-0": `# 12 HDMI 辅助板方案
对应电子书章节:\`如果需要直播\`\`业务场景:无人直播\`
## 方案价值
- 把外部视频信号稳定送进安卓端“摄像头”
- 满足 TikTok 对移动端直播形态的偏好
## 硬件清单
- Linux 辅助板(如 R3S
- 视频采集卡
- HDMI 线
- 安卓开发板(主控)
## 连接示意
\`素材源 -> HDMI -> 采集卡 -> 安卓板 USB\`
确认安卓相机可预览即代表链路打通。
`,
"4-1": `# 13 多设备群控与远控
对应电子书章节:\`远程控制\`\`内网穿透 frp\`\`应用多开/系统多开\`
## 目标
- 一台电脑管理多台安卓设备
- 降低人工重复操作成本
## 常见工具链
- qtscrcpy / escrcpy图形远控与群控
- frp-panel外网访问与设备穿透
- 安卓虚拟机:低成本扩展多开能力
## 交付标准
- 可以批量连接设备并执行统一操作
- 关键节点支持日志与异常回溯
`,
};
export type LessonDetail = {
name: string;
duration: string;
videoUrl: string;
markdown: string;
};
export const LESSONS_MAP: Record<string, LessonDetail> = {
"0-0": {
name: "01 从0到1安卓镜像安装与首次开机",
duration: "08:32",
videoUrl: "https://minioweb.hackrobot.cn/hackrobot/1. 安装安卓img_20260326_095335_848.mp4",
markdown: LESSON_MARKDOWNS["0-0"],
},
"0-1": {
name: "02 性能翻倍:安卓存储扩容实操",
duration: "12:15",
videoUrl: "https://minioweb.hackrobot.cn/hackrobot/2. 安卓扩容_20260326_095426_1002.mp4",
markdown: LESSON_MARKDOWNS["0-1"],
},
"1-0": {
name: "03 竖屏直播必备:系统显示方向设置",
duration: "15:20",
videoUrl: "https://minioweb.hackrobot.cn/hackrobot/3. 设置竖屏_20260326_095414_979.mp4",
markdown: LESSON_MARKDOWNS["1-0"],
},
"1-1": {
name: "04 一次搞定 RootMagisk + 模块安装",
duration: "18:00",
videoUrl: "https://minioweb.hackrobot.cn/hackrobot/4. 安装magisk以及模块_20260326_095427_1018.mp4",
markdown: LESSON_MARKDOWNS["1-1"],
},
"1-2": {
name: "05 防风控第一步:机型参数改造",
duration: "14:30",
videoUrl: "https://minioweb.hackrobot.cn/hackrobot/5. 修改机型参数_20260326_095438_1051.mp4",
markdown: LESSON_MARKDOWNS["1-2"],
},
"2-0": {
name: "06 降低风控命中GPS 精准定位",
duration: "16:00",
videoUrl: "https://minioweb.hackrobot.cn/hackrobot/6. gps定位_20260326_095430_1031.mp4",
markdown: LESSON_MARKDOWNS["2-0"],
},
"2-1": {
name: "07 无人直播入门:录播素材循环推流",
duration: "12:45",
videoUrl: "https://minioweb.hackrobot.cn/hackrobot/7无人直播-录播_20260326_100335_2814.mp4",
markdown: LESSON_MARKDOWNS["2-1"],
},
"2-2": {
name: "08 TikTok 实战开播:完整流程演示",
duration: "11:20",
videoUrl: "https://minioweb.hackrobot.cn/hackrobot/8. tiktok直播_20260326_095441_1064.mp4",
markdown: LESSON_MARKDOWNS["2-2"],
},
"2-3": {
name: "09 TikTok 进阶优化:稳定推流与细节调优",
duration: "20:00",
videoUrl: "https://minioweb.hackrobot.cn/hackrobot/9. tiktok直播-2_20260326_095403_943.mp4",
markdown: LESSON_MARKDOWNS["2-3"],
},
"3-0": {
name: "10 软路由与科学上网",
duration: "13:00",
videoUrl: SAMPLE_VIDEO,
markdown: LESSON_MARKDOWNS["3-0"],
},
"3-1": {
name: "11 静态住宅 IP 配置",
duration: "10:30",
videoUrl: SAMPLE_VIDEO,
markdown: LESSON_MARKDOWNS["3-1"],
},
"4-0": {
name: "12 HDMI 辅助板方案",
duration: "15:00",
videoUrl: SAMPLE_VIDEO,
markdown: LESSON_MARKDOWNS["4-0"],
},
"4-1": {
name: "13 多设备群控与远控",
duration: "12:00",
videoUrl: SAMPLE_VIDEO,
markdown: LESSON_MARKDOWNS["4-1"],
},
};
export function getLessonKey(moduleIndex: number, lessonIndex: number): string {
return `${moduleIndex}-${lessonIndex}`;
}
export function getLesson(moduleIndex: number, lessonIndex: number): LessonDetail | null {
return LESSONS_MAP[getLessonKey(moduleIndex, lessonIndex)] ?? null;
}