某教育平台视频下载完整指南

某教育平台视频下载完整指南

目标页面信息

项目 内容
页面 坐位体前屈(小学体育与健康 · 一至二年级 · 水平一 · 人教版)
页面URL https://basic.smartedu.cn/syncClassroom/classActivity?activityId=d1a5243d-9b85-11ec-92ef-246e9675e50c&chapterId=b44e3dad-30a0-3b26-8856-4db325da2a89&teachingmaterialId=96297c58-094b-45c8-b4da-4034ee696824&fromPrepare=1&classHourId=lesson_1
ActivityId d1a5243d-9b85-11ec-92ef-246e9675e50c
视频ID 5520fef9-9ba6-11ec-9c6b-fa20200f090a
视频封面 https://r1-ndr.ykt.cbern.com.cn/edu_product/esp/micro_lesson_video/5520fef9-9ba6-11ec-9c6b-fa20200f090a.t/zh-CN/1769440331675/transcode/thumbnail.jpg
视频格式 HLS(m3u8 流媒体)
CDN r1-ndr.ykt.cbern.com.cn / r3-ndr.ykt.cbern.com.cn

一、平台架构分析

1.1 前端技术栈

该平台是一个 SPA(单页应用),使用 React + Webpack 构建,打包名为 webpackChunkbase_template

核心 JS 文件:

文件 用途
chunk-fish-382dd43b.js 富文本编辑器、视频播放器封装
chunk-uc-4d4ca6ab.js UC(用户中心)SDK,负责认证
chunk-react-3842c9f4.js React 框架
chunk-vendors-7c5b0ad5.js 第三方库(video.js 6.13.0、HLS.js 等)
app-2c2376a1.js 主应用代码(1.3MB)

classActivity 页面加载的 Webpack Chunks:

1
2
7937, 4308, 7167, 112, 8937, 1562, 1463, 9743, 2620, 2269, 1644, 8844, 8444, 5735,
9053, 233, 8952, 3209, 436, 9168, 4879, 8146, 6344, 8565, 1707, 9629, 8419, 5886, 2122, 9284, 1070, 2846

关键模块:

  • Module 922846(chunk 2846): classActivity 页面主组件
  • Module 760753(chunk 2846): courseInfo Hook,负责获取课程数据
  • Module 210436(chunk 436): API 服务层,包含所有数据请求函数
  • Chunk 1644: 视频播放器组件(VideoPlayer、QuestionVideoPlayer 等)

1.2 视频播放器

  • 基于 video.js 6.13.0 + HLS.js
  • 播放器 JS/CSS 托管在 gcdncs.101.com/v0.1/static/fish/videoplayer/0.2.15/
  • 通过 beforeRequest 钩子为 HLS 请求添加 Authorization 头
  • 支持画质切换:默认、高清(720p)、标清(480p)、流畅(360p)

1.3 CDN 与资源存储

主机 用途
basic.smartedu.cn 主站
bdcs-file-2.ykt.cbern.com.cn NDR 静态资源(公开)
r{1,2,3}-ndr-private.ykt.cbern.com.cn NDR 私有资源(需认证)
r{1,2,3}-ndr.ykt.cbern.com.cn NDR 公开资源
ndvideo-key.ykt.eduyun.cn AES-128 密钥服务器
gcdncs.101.com 静态资源配置文件

二、视频数据获取流程

2.1 API 端点

课程详情(此接口无需认证,返回完整的课程结构数据):

1
GET https://s-file-1.ykt.cbern.com.cn/zxx/ndrv2/national_lesson/resources/details/{activityId}.json

2.2 数据结构

以「坐位体前屈 · 第一课时」为例,JSON 结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"id": "d1a5243d-9b85-11ec-92ef-246e9675e50c",
"title": "坐位体前屈",
"ti_items": [
{
"ti_storage": {
"files": [
{
"file_id": "1f943ea8-...",
"format": "m3u8",
"resolution": "1920x1080",
"encryption": "drm",
"identification": true
}
]
}
}
]
}

2.3 视频资源 ti_items

视频资源包含 6 个 ti_item

# ti_file_flag 格式 分辨率 加密 需认证
0 href m3u8 1920×1080 DRM
1 href-360p-m3u8 m3u8 640×360 DRM
2 href-480p-m3u8 m3u8 852×480 DRM
3 href-720p-m3u8 m3u8 1280×720 DRM
4 href-m3u8 m3u8 1920×1080 DRM
5 thumbnail_1 jpg -

每个 ti_item 的关键字段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"ti_storage": "cs_path:${ref-path}/edu_product/esp/assets/5520fef9-.../videos/xxx.m3u8",
"ti_storages": [
"https://r1-ndr-private.ykt.cbern.com.cn/edu_product/esp/assets/.../xxx.m3u8",
"https://r2-ndr-private.ykt.cbern.com.cn/edu_product/esp/assets/.../xxx.m3u8",
"https://r3-ndr-private.ykt.cbern.com.cn/edu_product/esp/assets/.../xxx.m3u8"
],
"ti_file_flag": "href-m3u8",
"ti_format": "m3u8",
"custom_properties": {
"encryption": "drm",
"identification": true
}
}

关键发现ti_storage 字段的路径前缀 cs_path:${ref-path} 会被替换为 NDR 私有主机地址。


三、视频保护机制

3.1 第一层:访问认证(401 Unauthorized)

m3u8 文件托管在 NDR 私有 CDN(r*-ndr-private.ykt.cbern.com.cn),必须在 URL 中附带有效的 accessToken 参数。

1
https://r2-ndr-private.ykt.cbern.com.cn/.../xxx.m3u8?accessToken=XXXXX

accessToken 获取方式

  • 通过 UC(用户中心)SDK 的 uc.getToken() 获取
  • token 存储在 localStorage,key 为 ND_UC_AUTH-e5649925-441d-4a53-b525-51a2f1c4e0a8&ncet-xedu&token
  • token 结构包含 access_tokenmac_keydiff 等字段

token 生成代码(来自 app-2c2376a1.js):

1
2
3
4
var C = function() {
var e = c.uc.getToken();
return e ? e.access_token : null;
};

3.2 第二层:HLS AES-128 加密

m3u8 播放列表使用标准 HLS AES-128 加密:

1
#EXT-X-KEY:METHOD=AES-128,URI="https://ndvideo-key.ykt.eduyun.cn/v1/resource_keys/0a14015988184e288b16feffb73b805a",IV=0x00000000000000000000000000000000
  • 加密方式: AES-128(标准 HLS 加密,非 Widevine/FairPlay)
  • 密钥服务器: ndvideo-key.ykt.eduyun.cn
  • 密钥 ID: 0a14015988184e288b16feffb73b805a
  • IV: 全零(0x00000000000000000000000000000000
  • 分片数量: 124 个 .ts 文件
  • 每个分片时长: 10秒(最后一个 5.92秒)
  • 分片总大小: 约 232MB

3.3 密钥服务器认证(P.Key 计算)

密钥服务器 ndvideo-key.ykt.eduyun.cn 使用基于签名的认证机制。播放器在请求密钥时,需要携带正确的 noncesignaccessToken 参数。

与传统 MAC 签名方案的区别:原方案需要手动计算 HMAC-SHA256 签名并构造 Authorization 头,而本方案通过拦截密钥服务器的交互流程,自动计算出最终的 P.Key(即 AES 解密密钥),无需手动构造签名。

密钥请求流程

第一步:获取签名参数(signs)

播放器首先向密钥服务器的 /signs 端点请求签名参数:

1
GET https://ndvideo-key.ykt.eduyun.cn/v1/resource_keys/{keyId}/signs?accessToken={accessToken}

返回 JSON 包含 nonce 字段,例如:

1
2
3
{
"nonce": "1780806943750:enc88ohj"
}

第二步:计算 sign 值

使用 MD5 对 nonce + keyId 进行哈希,取前 16 个字符作为 sign:

1
2
// 需要 CryptoJS 库
var sign = CryptoJS.MD5(nonce + keyId).toString().substring(0, 16);

示例:

  • nonce = "1780806943750:enc88ohj"
  • keyId = "0a14015988184e288b16feffb73b805a"
  • sign = MD5("1780806943750:enc88ohj0a14015988184e288b16feffb73b805a").substring(0, 16)

第三步:获取加密密钥

使用计算出的 sign 请求密钥服务器,获取 AES 加密的密钥:

1
GET https://ndvideo-key.ykt.eduyun.cn/v1/resource_keys/{keyId}?nonce={nonce}&sign={sign}&accessToken={accessToken}

返回的响应体是一个经过 AES-ECB 加密的密钥字符串。

第四步:AES-ECB 解密得到 P.Key

使用 sign 作为密钥,对响应体进行 AES-ECB 解密:

1
2
3
4
5
6
7
var encrypted = response.key;  // 服务器返回的加密密钥
var decrypted = CryptoJS.AES.decrypt(
encrypted,
CryptoJS.enc.Utf8.parse(sign), // sign 作为 AES 密钥
{ mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }
);
var P_Key = decrypted.toString(CryptoJS.enc.Utf8);

P.Key 特征

  • 长度:16 字节(32 个十六进制字符)例如: “1ca2e7da4f3843e3”
  • 格式:纯十六进制字符串
  • 用途:作为 HLS AES-128-CBC 解密的密钥,对每个 .ts 分片进行解密

3.4 认证流程总结

1
2
3
4
5
6
7
8
9
10
11
浏览器 ──GET /signs?accessToken=XXX──▶ 密钥服务器
◀──返回 { nonce }──────────────

浏览器 ──计算 sign = MD5(nonce+keyId).substring(0,16)──▶ 本地

浏览器 ──GET /keyId?nonce=&sign=&accessToken=──▶ 密钥服务器
◀──返回加密密钥(AES-ECB)────────────────

浏览器 ──AES-ECB解密(密钥=sign)──▶ 得到 P.Key

浏览器 ──下载 .ts 分片 ──▶ AES-128-CBC 解密(密钥=P.Key, IV=全零) ──▶ 播放

四、m3u8 播放列表详情

4.1 已获取的有效 m3u8 URL

1
https://r1-ndr-private.ykt.cbern.com.cn/edu_product/esp/assets/5520fef9-9ba6-11ec-9c6b-fa20200f090a.t/zh-CN/1703758568544/videos/1f943ea8-82ee-4e8d-9693-7cd44e458221-1920x1080-true-e2fc96b320803614c423c7fa5d2e1565-a52ec2956b434eadb0ddc7883044afe5.m3u8

4.2 分片 URL 格式

1
https://r1-ndr-private.ykt.cbern.com.cn/.../video{001..124}.ts?accessToken=XXX

分片编号从 001 到 124,共 124 个分片。

4.3 分片可访问性验证

资源 状态 说明
播放列表 ✅ 可访问 可通过 accessToken 访问
分片 ✅ 可访问 可通过 accessToken 访问,单个约 1.8MB
密钥 ⚠️ 需认证 需要通过 P.Key 计算流程获取
缩略图 ⚠️ 需认证 需要 accessToken

五、一种可实现的下载方式

基于对平台架构的深入分析,以下是经过验证的完整下载方案。

注意:如果你觉得手动操作麻烦,可以安装脚本实现(几乎)全自动方案。详见「常见问题 → Q: 有没有更简单的自动化方案?」。

前提条件

你需要以下工具:

工具 用途 安装方式
浏览器 + 篡改猴(自行搜索安装方式) 获取 P.Key、m3u8 和 token 自行安装即可
N_m3u8DL-RE 下载、解密 m3u8 见下方

安装下载器:点此下载 N_m3u8DL-RE

第一步:获取 P.Key (不想动手可以翻到下面看第二个QA)

  1. 打开目标课程页面,在浏览器中确保视频可以正常播放
  2. 按 F12 打开浏览器控制台
  3. 切换到「源代码」(Sources) 标签
  4. 搜索 P.key,定位到 videoplayer.min.js 文件
  5. 找到如下代码段:
1
2
3
4
5
6
7
O = P.key,
L = h.default.AES.decrypt(O, E, {
mode: h.default.mode.ECB,
padding: h.default.pad.Pkcs7
}),
j = L.toString(h.default.enc.Utf8),
n(i(j));
  1. n(i(j)); 这一行设置断点

  2. 回到控制台,输入 j 并回车,即可显示 P.Key 值

    P.Key 示例1ca2e7da4f3843e3

    以下图示(图片加载较慢,稍等)

    pmesQ2t.png

    pmesmUH.png

    pmeseVe.png

    pmeslxP.png

    这一串就是P.key

第二步:获取 M3U8 地址和 Token

2.1 获取M3U8地址。

在刚才获取P.key的基础上,在控制台切换到网络页面,按图操作。

pmesVbD.png

2.2获取Token

pmesKPA.png

另一种取 Token的方式

手动在控制台执行:

1
localStorage.getItem("ND_UC_AUTH-e5649925-441d-4a53-b525-51a2f1c4e0a8&ncet-xedu&token")

从返回的 JSON 中提取 access_token 字段的值。

第三步:拼接数据

先来回顾我们找到的三条数据:

P.key :1ca2e7da4f3843e3

M3U8地址https://r1-ndr-private.ykt.cbern.com.cn/edu_product/esp/assets/5520fef9-9ba6-11ec-9c6b-fa20200f090a.t/zh-CN/1703758568544/videos/1f943ea8-82ee-4e8d-9693-7cd44e458221-1920x1080-true-e2fc96b320803614c423c7fa5d2e1565-a52ec2956b434eadb0ddc7883044afe5.m3u8

Token:access_token:”7F938B205F876FC39BD5FD64A3C821679F8BC3B1766BEEDAF8D5DEDE5F005641C913C19A3083B606241F6C5E5D9F0DF42571E59BC6ED4B77”

将 m3u8 地址和 accessToken 拼接成最终地址:

1
{m3u8地址}?accessToken={accessToken}

示例:

1
https://r1-ndr-private.ykt.cbern.com.cn/edu_product/esp/assets/5520fef9-9ba6-11ec-9c6b-fa20200f090a.t/zh-CN/1703758568544/videos/1f943ea8-82ee-4e8d-9693-7cd44e458221-1920x1080-true-e2fc96b320803614c423c7fa5d2e1565-a52ec2956b434eadb0ddc7883044afe5.m3u8?accessToken=7F938B205F876FC39BD5FD64A3C821679F8BC3B1766BEEDAF8D5DEDE5F005641C913C19A3083B606241F6C5E5D9F0DF42571E59BC6ED4B77

第四步:下载视频

使用 N_m3u8DL-RE 下载并解密:

1
2
3
4
N_m3u8DL-RE "拼接好的m3u8地址" \
--saveDir "C:\Downloads" \
--saveName "坐位体前屈" \
--key-hex "P_Key的值"

示例:

1
2
3
4
N_m3u8DL-RE "https://r1-ndr-private.ykt.cbern.com.cn/...xxx.m3u8?accessToken=XXX" \
--saveDir "C:\Downloads" \
--saveName "坐位体前屈" \
--key-hex "1ca2e7da4f3843e3"

下载完成后,得到可播放的 .mp4 文件。

完整流程一览

1
2
3
4
5
6
7
8
9
10
11
12
13
┌─────────────────────────────────────────────────┐
│ 1. 在浏览器中打开课程页面并登录 │
│ ↓ │
│ 2. 安装脚本 → 自动获取 P.Key │
│ ↓ │
│ 3. 播放视频 → 自动捕获 m3u8 和 Token │
│ ↓ │
│ 4. 拼接: m3u8_url + "?accessToken=" + token │
│ ↓ │
│ 5. N_m3u8DL-RE 输入地址与 P.Key 下载 │
│ ↓ │
│ ✅ 得到可播放的 .mp4 文件 │
└─────────────────────────────────────────────────┘

常见问题

Q: accessToken 会过期吗?

会。 accessToken 有效期通常为 2-4 小时。如果下载过程中提示 401,需要重新获取 token。

Q: 下载的视频无法播放?

检查以下几点:

  • 是否使用了 --key-hex 提供正确的 P.Key
  • P.Key 是否是 16 字节(32 个十六进制字符)
  • N_m3u8DL-RE 输出日志中是否有解密错误

Q: 有没有更简单的自动化方案?

可以安装篡改猴(Tampermonkey)脚本,实现(几乎)全自动下载:

  1. 自动获取 m3u8、accessToken,并进行拼接
  2. 自动拦截密钥服务器交互,计算 P.Key
  3. 内置一键下载功能(2GB 以内视频直接下载,超过 2GB 使用外部下载器)

脚本安装步骤

  1. 安装篡改猴浏览器扩展(自行搜索安装方法)
  2. 下载脚本:下载地址(已打包好篡改猴插件)
  3. 安装脚本后,进入课程视频页面,脚本会自动捕获所有参数
  4. 点击面板上的「一键下载 MP4」即可

注意:脚本内置下载仅支持 2GB 以内的视频,超过此大小请使用 N_m3u8DL-RE 下载器。


附录

智慧教育平台教材下载

链接格式:

普通教材:

1
https://basic.smartedu.cn/tchMaterial/detail?contentType=assets_document&contentId=(教材contentId)&catalogType=tchMaterial&subCatalog=tchMaterial

资源包教材:

1
https://basic.smartedu.cn/tchMaterial/detail?contentType=thematic_course&contentId=(教材contentId)&catalogType=tchMaterial&subCatalog=tchMaterial

解析接口:

普通教材:

1
https://s-file-2.ykt.cbern.com.cn/zxx/ndrv2/resources/tch_material/details/(教材contentId).json

资源包教材:

1
https://s-file-1.ykt.cbern.com.cn/zxx/ndrs/special_edu/thematic_course/(教材contentId)/resources/list.json

英语听力:

1
https://s-file-1.ykt.cbern.com.cn/zxx/ndrs/resources/(教材contentId)/relation_audios.json

示例网页链接:

普通教材:

1
https://basic.smartedu.cn/tchMaterial/detail?contentType=assets_document&contentId=bdc00134-465d-454b-a541-dcd0cec4d86e&catalogType=tchMaterial&subCatalog=tchMaterial

资源包教材:

1
https://basic.smartedu.cn/tchMaterial/detail?contentType=thematic_course&contentId=2afcdb56-6fce-8c99-0bc9-e9dd33b5c51c&catalogType=tchMaterial&subCatalog=tchMaterial

智慧教育平台课程资源下载

链接格式:

教育部资源:

1
https://basic.smartedu.cn/syncClassroom/classActivity?activityId=(资源包activityId)&chapterId=&teachingmaterialId=&fromPrepare=0

学校提供网课:

1
https://basic.smartedu.cn/qualityCourse?courseId=(资源包teachingmaterialId)&chapterId=&teachingmaterialId=&fromPrepare=0&classHourId=lesson_1

备课:

1
https://basic.smartedu.cn/syncClassroom/prepare/detail?lessonId=(资源包lessonId)&chapterId=&teachingmaterialId=&fromPrepare=1&classHourId=lesson_1

解析接口:

需要带上 X-Nd-Auth 标头

教育部资源:

1
https://s-file-1.ykt.cbern.com.cn/zxx/ndrv2/national_lesson/resources/details/(资源包activityId).json

学校提供网课:

1
https://s-file-2.ykt.cbern.com.cn/zxx/ndrv2/resources/(资源包teachingmaterialId).json

备课:

1
https://s-file-2.ykt.cbern.com.cn/zxx/ndrv2/prepare_lesson/resources/details/(资源包lessonId).json

示例网页链接:

教育部资源:

1
https://basic.smartedu.cn/syncClassroom/classActivity?activityId=f15feef1-b908-44f5-a765-500b9395c313&chapterId=8d6cc118-a169-3ea1-9a45-31cc841ad239&teachingmaterialId=4a4aa279-8dc6-4098-b45f-dd3f7d5a61b2&fromPrepare=0

学校提供网课:

1
https://basic.smartedu.cn/qualityCourse?courseId=8ae7e48f-842c-12fc-0184-35dacdee016f&chapterId=8ae5c0d4-cfd4-34d1-9757-0295bd0c55ed&teachingmaterialId=4a4aa279-8dc6-4098-b45f-dd3f7d5a61b2&fromPrepare=0&classHourId=lesson_1

备课:

1
https://basic.smartedu.cn/syncClassroom/prepare/detail?lessonId=8aee80a5-6b86-5bc9-016b-87465e6e0290&chapterId=5bb731e1-cdac-3984-a977-3d44c5d2d809&teachingmaterialId=4a4aa279-8dc6-4098-b45f-dd3f7d5a61b2&fromPrepare=1&classHourId=lesson_1