diff --git a/docs/课程上线指南.md b/docs/课程上线指南.md new file mode 100644 index 0000000..146631a --- /dev/null +++ b/docs/课程上线指南.md @@ -0,0 +1,247 @@ +# 课程上线指南 + +面向「要往小程序里上一门新课」的人。按第 7 节的顺序做,每一步都有验证方法。 + +--- + +## 0. 先理解三件事 + +1. **数据分两处**:课程 / 课时的**文字信息**在数据库里(管理后台改的就是它); + **视频和图片**在数据端,是磁盘上的文件。 +2. **付费课时不是 mp4**,是 HLS 目录(一个 `playlist.m3u8` + 一堆 `.ts` 分片)。 +3. **媒体文件不在云服务器上**。数据域名整站反代到隧道另一端(NAS),服务端播放视频也走同一条路。 + 所以文件最终要落到 NAS 上对应的目录里,放对位置就立刻生效。 + +> ⚠️ 目前**还没有后台上传功能**。文件要手工(或打包成 zip)放到 NAS 的对应目录, +> 再回管理后台填表单。 + +--- + +## 1. 三个不能随便起的名字 + +这三个名字一旦定下来就**不要改**,它们是文件路径的一部分: + +| 名字 | 同时还是 | +|---|---| +| `courses.code` | 视频目录名 `Course/{code}/` 和封面名 `Course/{code}.png` | +| `lessons.chapter_id` | 目录中的一级:`Course/{code}/{chapter_id}/` | +| `lessons.play_id` | 目录中的二级:`Course/{code}/{chapter_id}/{play_id}/` | + +改其中任何一个,已经发出去的播放地址、封面、以及用户端的缓存全部失效。 + +另外一批名字是**拼出来的**,由后台填的文字决定,同样不能随手改: + +| 小程序里的地址 | 由什么拼出来 | +|---|---| +| `Course/{code}.png` | 课程 `code` | +| `Images/NewCourse/{条目名}.png` | 首页新课的 `course_name` | +| `Images/FreeCourse/{条目名}.png` | 首页试听的 `course_name` | +| `Images/Banner/{文件名}` | 轮播图的 `filename`(含扩展名) | +| `WebPages/{网页id}.png` | 文章的 `legacy_page_id` | + +现网示例:`code` 是 64 位十六进制,`chapter_id` 是 `1`、`2`、`3`, +`play_id` 是 13 位十六进制(如 `65eefb35e29da`)。 + +--- + +## 2. 要准备的文件 + +假设新课的 `code` 是 `AbC...`(64 位),有三个章节、每章两个课时: + +``` +Course/AbC....png ← 课程封面,1 张 +Course/AbC.../1/65eefb35e29da/playlist.m3u8 ← 课时 1 +Course/AbC.../1/65eefb35e29da/0000.ts 0001.ts ... ← 该课时的分片 +Course/AbC.../1/65eefb35e29db/playlist.m3u8 ← 课时 2 +Course/AbC.../2/65eefb50f02b5/playlist.m3u8 ← 课时 3 +... +``` + +如果这门课还要上首页,另外准备: + +| 用途 | 文件 | 引用方式 | +|---|---|---| +| 首页「新课上线」列表图 | `Images/NewCourse/{条目名}.png` | 按 `course_name` 拼 | +| 首页「新课上线」长图内页 | `Images/NewCourseWebPages/{分组名}/{n}.png` | 写在长图 HTML 里 | +| 首页「试听」列表图 | `Images/FreeCourse/{条目名}.png` | 按 `course_name` 拼 | +| 免费试听视频 | `FreeVideos/{任意目录}/{任意名}.m3u8` + 分片 | 路径存在数据库字段里 | + +**图片规格参考** + +- 课程封面:现网是 **900×500**(后台卡片按 9:5 显示,整张完整展示不裁剪)。 +- 新课长图:现网每张 **800px 宽**,高度按内容(实测一张 800×564)。所有图宽度保持一致。 +- 首页列表图:按名称拼 `.png`,建议和长图同宽或按 2 倍图准备。 + +--- + +## 3. HLS 打包要求 + +现网一个真实的 `playlist.m3u8`: + +``` +#EXTM3U +#EXT-X-VERSION:3 +#EXT-X-TARGETDURATION:4 +#EXT-X-MEDIA-SEQUENCE:0 +#EXTINF:4.166667, +0000.ts +#EXTINF:4.166667, +0001.ts +``` + +四条硬要求: + +1. **分片引用必须是相对路径**(`0000.ts`),不能写 `http://...` 绝对地址。 + 服务端会给每一行分片单独签名,绝对地址会被跳过——防盗链直接失效,播放器还会绕过服务端直连。 +2. **文件名必须叫 `playlist.m3u8`**。这个名是服务端写死的,不是数据库里的字段。 +3. **目录名只用字母数字**。`chapter_id` / `play_id` 参与播放地址的拼接与签名,中文和空格容易踩坑。 +4. **目录里不要放无关文件**,整棵目录树都在签名保护下。 + +其余参数(切片时长、编码、加密)没有强制要求,按上面的形状切即可。 +如果做了 HLS 加密,`.key` 文件要放在同一目录树内,`EXT-X-KEY` 的 URI 也用相对路径。 + +--- + +## 4. 首页新课长图的内容要求 + +「长图」不是一张图片文件,而是 `home_courses.content` 里的一段 **HTML**, +服务端原样返回,小程序用内置的 Parser 组件渲染。现网真实内容: + +```html +
+
+
+
+
` 必须带 `font-size: 0`**(通常还带 `margin:0; padding:0`)。少了它,图与图之间会露出白缝,长图就断了。
+3. **不要给 `` 写 width / height**。渲染时会自动补 `width:100%` 按屏宽缩放;写死像素宽度会溢出屏幕。
+4. **底部要留白**。长图页底部钉着两个悬浮按钮(购买 / 咨询客服),末尾留一段空白,否则按钮压住内容。
+5. **只能放静态标签**。`