从一个小痛点到可用网站:我如何通过 ChatGPT 和 GitHub 做出一个 VOA 英语播放器

最近我一直在思考一件事:

现在使用 ChatGPT,是否已经可以不打开 IDE,只通过聊天完成一个真正可用的小项目?

这次我实际尝试了一遍。

最后的结果是,我通过和 ChatGPT 持续对话,做出了一个可以在手机上使用的 VOA 英语学习播放器。

项目已经上线:

它现在包含 VOA Learning English Level 2 的全部 30 课,支持:

  • 视频播放;
  • 音频播放;
  • 完整英文对话文本;
  • 0.70、0.80、0.90 等细粒度倍速;
  • 整课循环;
  • 15、30、45、60 分钟睡眠定时;
  • 记忆播放进度;
  • 手机阅读字幕时,播放器自动固定在屏幕底部。

整个项目并不复杂,但它很好地展示了一种新的开发方式:

人负责发现真实问题、决定产品边界和验收结果,AI 负责需求整理、编码、测试、提交和部署。


一、这个项目来源于一个非常具体的问题

我最近想通过 VOA Learning English 提高英语听力。

其中的 Let’s Learn English – Level 2 系列非常适合我。

VOA 原网站已经提供了:

  • 视频;
  • 音频;
  • 完整对话文本;
  • 浏览器直接播放;
  • 手机息屏播放。

内容本身已经很好,我并不需要重新制作一套英语课程。

但是使用一段时间后,我遇到了几个问题。

1. 播放速度不够灵活

对于英语学习来说,1 倍速有时候稍快。

我更希望使用:

0.70
0.75
0.80
0.85
0.90

但 VOA 原页面没有提供这样的细粒度速度控制。

2. 不能循环播放

有些课程我希望连续听很多遍,尤其是在睡前、走路或者做其他事情的时候。

原页面没有“整课循环”功能。

3. 没有睡眠定时

我希望设置:

30 分钟后自动停止

这样睡着以后不会一直播放。

4. 阅读字幕时,播放器会滚出屏幕

刚开始学习一节课时,我经常需要一边听,一边向下阅读 Conversation。

这时可能需要:

  • 暂停;
  • 继续;
  • 后退 10 秒;
  • 重复听一句。

但向下滚动以后,原来的音频播放器已经不在屏幕里了。


二、我最开始也考虑过现成方案

一开始,我并没有马上决定开发网站。

我先考虑了几个更简单的方法。

VLC

可以把音频下载下来,用 VLC 播放。

VLC 支持:

  • 自定义速度;
  • 循环;
  • A-B 循环;
  • 睡眠定时。

功能上基本没有问题。

但是课程管理体验不好。

下载后的文件名不直观,音频、视频和字幕分散在不同地方。课程多了以后,很难快速知道自己学到哪一课。

Apple Podcasts 和 RSS

VOA 提供 RSS,我也尝试把它加入 Podcast 客户端。

但实际测试发现,RSS 内容并不完整,客户端里只显示了很少的内容,不能作为完整课程目录使用。

VLC 加 iPhone 系统计时器

也可以在 VLC 播放,然后再打开 iPhone 时钟,设置“30 分钟后停止播放”。

这个方案能用,但每次都需要在多个 App 之间来回操作。

单独看每一步都不复杂,但长期使用时,这种摩擦会让人越来越不愿意打开它。

于是我意识到,我真正需要的并不是更多功能,而是:

把 VOA 已经存在的视频、音频和文本,放进一个更适合我学习习惯的页面里。


三、第一件重要的事情:限制项目范围

刚开始说“自己做一个英语学习网站”时,这个概念很容易无限扩大。

例如可以继续想到:

  • 用户登录;
  • 云同步;
  • 生词本;
  • AI 翻译;
  • 单句字幕高亮;
  • 跟读录音;
  • 发音评分;
  • 学习统计;
  • 原生 App;
  • 离线下载。

这些功能都可能有价值。

但如果第一版就做这些,这个项目很可能几个月都无法真正使用。

所以我和 ChatGPT 先把 MVP 压缩成几个核心功能:

第一版需要 第一版不做
课程选择 用户账号
视频播放 数据库
音频播放 云同步
完整对话文本 AI 翻译
细粒度倍速 单句时间轴
整课循环 发音评分
睡眠定时 原生 App
本地进度保存 后台管理系统

这个决策非常重要。

最终项目甚至明确规定:

  • 不使用 React;
  • 不使用 Vue;
  • 不使用 Next.js;
  • 不使用数据库;
  • 不使用后端;
  • 不使用 Docker;
  • 不在用户访问时实时抓取 VOA;
  • 不重新实现音频播放引擎。

项目运行时只使用原生 HTML、CSS、JavaScript、静态 JSON 和浏览器的 localStorage

这也是这次项目能够快速完成的主要原因之一。


四、技术架构比想象中简单

整个项目的主要目录大致如下:

voa-level-2/
├── index.html
├── css/
│   └── styles.css
├── js/
│   ├── app.js
│   └── core.js
├── data/
│   └── lessons.json
├── scripts/
│   └── import_voa.py
├── tests/
├── docs/
│   └── REQUIREMENTS.md
├── AGENTS.md
└── .github/
    └── workflows/

运行时没有服务器。

访问页面时,浏览器只是读取:

index.html
styles.css
app.js
lessons.json

然后直接从 VOA 的媒体地址播放视频和音频。

这意味着它非常适合部署到 GitHub Pages。


五、为什么坚持使用浏览器原生播放器

音频部分直接使用:

<audio controls preload="metadata"></audio>

视频部分使用:

<video controls playsinline preload="metadata"></video>

没有引入第三方播放器,也没有自己绘制进度条。

这样做有几个重要好处。

第一,iPhone 兼容性更可靠

Safari 对原生 <audio><video> 的支持通常比自定义播放方案更稳定。

浏览器原生播放器负责:

  • 播放;
  • 暂停;
  • 拖动进度;
  • 缓冲;
  • 锁屏播放;
  • 系统媒体控制;
  • 音频焦点。

项目只补充 VOA 原页面没有的功能。

第二,代码更少

调整速度只需要:

audio.playbackRate = 0.8;
audio.preservesPitch = true;

整课循环只需要:

audio.loop = true;

前进和后退只需要修改:

audio.currentTime += 10;
audio.currentTime -= 10;

项目没有必要为了视觉上的统一,再重新实现一个播放器。


六、第一次开发:先只做三课

第一版没有马上导入全部 30 课。

我们先放入了 Lesson 1、Lesson 2 和 Lesson 3。

原因很简单:

在没有验证手机播放体验之前,先不要花时间整理全部数据。

前三课已经足够验证:

  • 视频是否可以播放;
  • 音频是否可以播放;
  • 0.8 倍速是否正常;
  • 循环是否正常;
  • 睡眠定时是否正常;
  • 字幕阅读是否舒服;
  • 播放位置是否能够保存;
  • GitHub Pages 是否能够正常运行。

我开启 GitHub Pages 后,在手机上实际使用了一遍。

结果比我预期的还好。

页面已经基本就是我想要的工具:

选择课程
   ↓
播放音频或视频
   ↓
调整为 0.8 倍速
   ↓
开启循环
   ↓
设置 30 分钟停止
   ↓
向下阅读完整对话

这一步非常关键。

不是先把所有功能做完,再一次性验收,而是:

先完成最小闭环,实际使用,再根据真实感受继续修改。


七、第二次开发:自动导入全部 30 课

前三课验证通过后,我提出了第二个要求:

  1. 把视频升级到 1080p;
  2. 补齐剩余课程。

ChatGPT 随后修改了 Python 导入脚本。

导入器会读取 VOA 的课程索引页,然后进入每一节课的页面,提取:

  • 课程编号;
  • 课程标题;
  • VOA 原页面地址;
  • 1080p 视频地址;
  • 128 kbps 音频地址;
  • 完整 Conversation 文本。

生成的数据结构类似:

{
  "id": 1,
  "title": "Budget Cuts",
  "sourceUrl": "https://learningenglish.voanews.com/...",
  "videoUrl": "https://..._1080p.mp4",
  "videoQuality": 1080,
  "audioUrl": "https://..._hq.mp3",
  "audioFormat": "mp3",
  "audioBitrate": 128,
  "transcript": [
    {
      "speaker": "Anna",
      "text": "..."
    }
  ]
}

网站本身不会在用户打开页面时抓取 VOA。

抓取只在开发阶段运行一次,然后把结果保存到:

data/lessons.json

这样做有几个好处:

  • 页面打开速度更快;
  • 不依赖 VOA 页面 HTML 结构;
  • 即使 VOA 页面稍后改版,现有网站仍然可以读取静态数据;
  • GitHub Pages 不需要后端;
  • 数据变化可以通过 Git diff 审查。

现在项目已经包含连续的 Lesson 1–30,并使用 VOA 提供的 1080p 视频。


八、一个真实的小问题:Lesson 8 没有独立 MP3

在导入全部课程时,还遇到了一个特殊情况。

大多数课程都提供:

128 kbps MP3

但 Lesson 8 的页面没有独立 MP3。

最终没有从第三方网站寻找音频,也没有伪造一个地址,而是采用了一个明确记录的回退方案:

  • 视频播放器继续使用 1080p 视频;
  • 音频播放器使用 VOA 240p MP4 文件中的音轨;
  • 数据中记录它是 MP4 音轨回退,而不是 MP3。

例如:

{
  "audioFormat": "mp4",
  "audioFallbackVideoQuality": 240
}

这件事虽然很小,但它体现了一个重要原则:

数据异常不能静默忽略,而应该明确记录。


九、第三次开发:让播放器跟随字幕固定在底部

完整课程上线以后,我实际使用时又发现了一个问题。

在手机上听音频时,往下滚动阅读字幕,播放器会离开屏幕。

而刚开始学习一课时,我经常需要:

  • 暂停;
  • 继续;
  • 后退 10 秒;
  • 拖动进度。

VOA 原网站在手机上有一个不错的行为:

页面往下滚动时,播放器会固定在屏幕底部。

所以我把这个需求告诉了 ChatGPT。

最终确定的界面是:

┌────────────────────────────────┐
│ Lesson 3 · He Said…   ↶10   10↷ │
│ [ ▶ ━━━━━━━━━━━━━ 03:21 / 05:00 ]│
└────────────────────────────────┘

十、这个浮动播放器不是第二个播放器

实现这个功能时,最重要的技术决定是:

页面中始终只有一个 <audio>

最容易想到的做法,是在原页面放一个播放器,然后再在底部复制一个迷你播放器。

但这样会产生很多问题:

  • 两个播放器的进度需要同步;
  • 两个播放器的暂停状态需要同步;
  • 两个播放器的倍速需要同步;
  • 两个播放器可能同时播放;
  • iPhone 锁屏控制可能连接到错误的播放器;
  • 切换课程时可能残留上一个音频;
  • 同一个媒体文件可能被重复请求。

所以最终采用的方式是:

同一个音频元素,在正常状态下位于页面中;滚动后只是通过 CSS 改成 position: fixed

HTML 结构大致如下:

<div id="audioDockSlot">
  <div id="audioDock">
    <div class="audio-dock-toolbar">
      <span>Lesson 3 · He Said - She Said</span>
      <button>↶10</button>
      <button>10↷</button>
    </div>

    <audio id="audioPlayer" controls></audio>
  </div>
</div>

当播放器需要固定时:

.audio-dock.is-docked {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
}

项目提交中明确增加了手机底部播放器,并保持复用同一个原生 <audio>


十一、浮动播放器看起来简单,实际有几个细节

1. 不能简单使用 position: sticky

因为播放器在媒体卡片中,而字幕在另一个卡片中。

sticky 通常会受到父容器边界限制,无法一直跟随用户阅读后面的字幕。

所以需要使用:

position: fixed;

2. 需要保留占位高度

元素变成 fixed 以后,会离开正常文档流。

如果不保留原来的高度,下面的页面会突然向上跳动。

所以外部保留了:

<div id="audioDockSlot">

浮动前记录播放器高度,浮动后让占位容器继续保持相同高度。

3. 不能直接观察浮动元素

页面使用 IntersectionObserver 判断原播放器是否已经滚出屏幕。

观察的是占位容器,而不是正在浮动的播放器本身。

否则可能出现:

滚出屏幕
→ 变为 fixed
→ 又进入屏幕
→ 取消 fixed
→ 再次滚出

最终造成播放器闪烁。

4. 暂停后不能消失

浮动条件不是:

audio.paused === false

因为用户在字幕区域暂停后,仍然需要马上继续播放。

所以播放器只要在当前课程中启动过一次,即使暂停,也会继续留在底部。

5. 需要适配 iPhone 底部安全区域

全面屏 iPhone 底部有 Home Indicator。

因此底部播放器使用:

env(safe-area-inset-bottom)

避免按钮和进度条贴住屏幕最底部。

6. 不能遮挡最后几句字幕

播放器浮动后,页面底部会增加额外 padding。

这样最后一段字幕仍然可以滚动到播放器上方。

7. Toast 提示也要上移

项目原本的提示信息也固定在页面底部。

当播放器出现时,Toast 需要移动到播放器上方,否则两者会重叠。


十二、ChatGPT 是如何直接参与开发的

这次和普通的“让 AI 生成一段代码”不太一样。

整个过程大致是:

发现真实问题
    ↓
通过聊天描述使用场景
    ↓
ChatGPT 整理需求文档
    ↓
限制技术栈和 MVP 范围
    ↓
读取 GitHub 仓库
    ↓
创建和修改项目文件
    ↓
创建分支和提交
    ↓
运行 GitHub Actions
    ↓
读取失败日志
    ↓
修复问题并重新提交
    ↓
合并到 main
    ↓
GitHub Pages 自动发布
    ↓
我在真实手机上使用
    ↓
继续提供下一轮反馈

我在这个过程中主要做三件事:

  1. 描述真实使用问题;
  2. 决定哪些功能现在做、哪些以后做;
  3. 在手机上验收最终体验。

ChatGPT 则完成了:

  • 需求整理;
  • 项目结构设计;
  • HTML、CSS 和 JavaScript;
  • Python 数据导入器;
  • 自动测试;
  • GitHub Actions;
  • Git 提交;
  • GitHub Pages 部署;
  • 失败日志分析;
  • 后续迭代修改。

十三、AI 开发并不是一次就成功

这次过程也并不是 ChatGPT 一次生成代码,然后所有东西都完美运行。

中间出现过几次失败。

例如:

  • 自动修改脚本因为空格和缩进不完全匹配而失败;
  • git diff --check 检测到尾随空格;
  • 浏览器测试脚本的模块模式不兼容;
  • 第一次浏览器验证失败后,需要重新调整工作流。

但不同的是,这些错误发生后,ChatGPT 可以继续:

  1. 查看 GitHub Actions 日志;
  2. 找到失败步骤;
  3. 修改脚本;
  4. 再次提交;
  5. 重新运行测试。

最后项目的正式 CI 成功通过。

GitHub Pages 部署也成功完成。

此外还运行了真实 Chromium 浏览器测试,验证手机宽度和桌面宽度下的浮动播放器行为。

这让我对 AI 编程有了一个更实际的理解:

AI 编程不是“永远不出错”,而是“发现错误、读取反馈、修改和重新验证的循环成本变得很低”。


十四、为什么这次开发体验比较好

我认为有几个关键原因。

1. 问题是真实存在的

这个项目不是为了测试 AI 而凭空想出的 Demo。

它来源于我每天可能真正使用的场景。

所以我很容易判断:

  • 什么功能重要;
  • 什么设计好用;
  • 什么功能没有必要;
  • 修改以后是否真的有价值。

2. 产品范围足够小

项目没有一开始就追求:

英语学习平台

而是只解决:

VOA Level 2 的播放体验

目标越具体,AI 越容易做出符合预期的结果。

3. 明确禁止过度工程化

项目一开始就明确:

不使用 React
不使用后端
不使用数据库
不使用 Docker
不使用用户系统

这避免了一个几百行就能解决的问题,被扩展成一个复杂系统。

4. 每次只增加一个真实需求

第一次:

视频 + 音频 + 字幕 + 倍速 + 循环 + 定时

第二次:

补齐 30 课 + 1080p

第三次:

字幕阅读时底部浮动播放器

每一轮都很明确。

5. 使用真实设备反馈

浏览器自动测试只能验证:

  • 元素是否存在;
  • CSS 是否生效;
  • 滚动后是否固定;
  • 按钮是否改变进度。

但 iPhone 的:

  • 息屏播放;
  • 锁屏控制;
  • 后台定时;
  • Home Indicator;
  • Safari 原生播放器布局;

仍然需要真实设备验证。

AI 可以完成大部分开发,但用户的真实体验仍然是最终标准。


十五、这种开发方式适合哪些项目

经过这次实践,我认为通过 ChatGPT 和 GitHub 直接开发,特别适合下面这些项目:

  • 个人工具;
  • 静态网站;
  • 内容整理页面;
  • 播放器;
  • 计算器;
  • 数据查看器;
  • 简单仪表盘;
  • 浏览器小工具;
  • 自动化脚本;
  • API 展示页面;
  • 小型 SEO 页面;
  • 已有内容的增强工具。

这些项目通常有几个共同特点:

  • 需求比较具体;
  • 数据结构清晰;
  • 不需要复杂权限;
  • 不保存高度敏感信息;
  • 可以通过自动测试和人工使用快速验收;
  • 可以部署到 GitHub Pages、Cloudflare Pages 或其他静态平台。

它暂时不太适合完全依赖聊天直接上线的场景包括:

  • 金融支付系统;
  • 医疗数据系统;
  • 复杂用户权限;
  • 大规模实时后端;
  • 高并发交易;
  • 存储大量隐私数据;
  • 安全边界复杂的企业平台。

这些项目仍然需要更系统的设计、安全审查、代码评审和运维方案。


十六、我对 AI 开发方式的新认识

过去使用 AI 编程时,我更习惯这样的流程:

我在本地打开项目
→ 把一段需求发给 AI
→ AI 返回代码
→ 我复制代码
→ 本地运行
→ 出错后再把错误复制给 AI

这次的体验不同。

ChatGPT 可以直接读取 GitHub 仓库,修改代码,提交分支,运行 CI,查看失败日志,再继续修复。

聊天窗口逐渐变成了一个更高层的开发界面。

我不再需要关心每一行代码怎么写,而是更多考虑:

  • 用户真正的问题是什么;
  • 第一版应该做到什么程度;
  • 哪些功能现在不应该做;
  • 验收标准是什么;
  • 使用以后哪里还不方便。

这并不意味着技术知识不再重要。

恰恰相反,技术判断会更多体现在:

  • 是否选择了合理的架构;
  • 是否控制住项目范围;
  • 是否知道哪里存在风险;
  • 是否建立了测试和回退方案;
  • 是否能够识别 AI 的过度设计。

十七、这次项目最重要的经验

这次实践让我认为,使用 ChatGPT 做小项目时,最有效的方式不是说:

帮我做一个英语学习网站

而是按照下面的顺序:

先描述真实场景

例如:

我在手机上学习 VOA。
我需要一边听音频,一边向下阅读字幕。
阅读字幕时需要经常暂停和后退。

再描述当前方案为什么不方便

例如:

VLC 文件不好管理。
RSS 内容不完整。
VLC 和系统定时器需要跨多个 App。

然后明确第一版边界

例如:

不需要登录。
不需要数据库。
不需要 AI 翻译。
只需要播放、倍速、循环、定时和字幕。

最后给出可验证的验收标准

例如:

手机滚动字幕时播放器固定在底部。
暂停以后播放器不能消失。
后退 10 秒按钮必须有效。
切换视频后浮动播放器自动收起。

需求越接近真实行为,AI 输出的产品越接近真正可用。


结语

这个项目本身并不宏大。

它只是解决了一个很小的问题:

让我能够更方便地使用 VOA Level 2 学习英语。

但它让我看到了一种很有意思的可能性。

未来,很多个人工具不一定需要先学习完整的前端框架,也不一定需要专门组建团队。

一个人可以通过聊天:

  • 说明问题;
  • 讨论方案;
  • 限制范围;
  • 让 AI 编码;
  • 自动测试;
  • 直接发布;
  • 实际使用;
  • 再继续迭代。

对我来说,最有价值的变化不是“AI 能写多少代码”,而是:

一个真实想法从产生到变成可使用产品,中间的阻力正在快速降低。

这次我没有试图做一个庞大的英语学习平台。

我只是解决了自己每天真实遇到的一个问题。

而这也许正是当前使用 AI 开发小项目,最适合的方式。

评论

还没有人评论,抢个沙发吧...

Viagle Blog

欢迎来到我的个人博客网站