Repository navigation
Classroom Video
ChangYo edited this page Oct 25, 2026
·
2 revisions
| 项 | 值 |
|---|---|
| 平台 | v.sjtu.edu.cn |
| 认证 | 校园 Web 会话(jAccount SSO Cookie) |
| 数据方向 | 只读 |
| 难度 | 中 |
| 最后验证 | 2026-10-06 |
| 证据等级 | A(实测) |
课堂视频(交大视频)是 Canvas「课堂视频新版」指向的平台,真实服务由教育技术中心提供。它提供本人教学班的录像、双路 MP4、逐句转写、AI 知识点和分段总结、PPT 逐页图片与 OCR。
- 可获取:课程、录像场次、双路 MP4、转写、AI 总结、PPT 图片与 OCR、播放数、最近观看。
- 前置:
v.sjtu.edu.cn的校园会话 Cookie。不需要 JWT、LTI 或 Canvas Token。 - 注意:未登录时接口返回空列表或空响应,不返回 401;转写和 PPT 异步生成;MP4 直链是签名 URL,不应缓存。
| 对象 | 内容 | 备注 |
|---|---|---|
| 课程/教学班 |
teclId、subjCode、subjName、学期 acteId、院系 |
课程代码可与 Canvas course_code 映射 |
| 录像场次 |
courseId、起止时间、教室 clroName、节次 letiNumber、教师、缩略图 |
含未来排课,需区分已录/未上 |
| 播放流 | 双路 MP4(viewNum 1/5)、vodTime 秒 |
签名 URL,每次现取 |
| 逐句转写 |
bg/ed(毫秒)、res 文本 |
异步,完成度滞后 |
| AI 总结 |
mindmap 知识点树 + timeRanges;documentSkims 分段总结 |
异步 |
| PPT | 逐页 imageUrl、ocrText、imageSeekTime、textKeywordList
|
图片免 Cookie;OCR 有噪声 |
| 观看数据 |
playCount、lastPlayInfoByTeclId
|
只读;不要上报/伪造 |
| 子系统 | Base URL | 鉴权 | 用途 |
|---|---|---|---|
| 课堂录像数据 API | https://v.sjtu.edu.cn/jy-application-resourcemanage/v1 |
仅 sjtu.edu.cn Cookie |
本文主体 |
| 统一身份 | https://v.sjtu.edu.cn/cloud-rbac |
Cookie | 登录判定、用户信息 |
| 视频门户(旁听/收藏/排行) | https://v.sjtu.edu.cn/jy-application-resmgr |
Cookie + jwt-token 头(localStorage) |
首版不需要 |
| Canvas LTI 侧 | https://v.sjtu.edu.cn/jy-application-canvas-sjtu |
LTI token | 不需要;直接调 v.sjtu 更简单 |
| 媒体域 | https://live.sjtu.edu.cn:443 |
URL 签名 key
|
MP4、PPT 图片、缩略图 |
前端页面 #/play-center?<加密串> 中的参数由 SPA 解析为 teclId(教学班 ID)。不需要解密该参数,从课程列表接口即可正向发现。[实测]
| 标识 | 含义 | 示例 |
|---|---|---|
teclId |
教学班 |
9000 = 计算理论 2026 秋 |
courseId / id
|
一场录像(一次课的一节) | 153907 |
acteId |
学期 |
11 = 2026-2027-1 |
letiNumber |
节次 | 8 |
vodTime |
时长(秒) | 3580 |
subjCode |
课程代码 |
CS3313;与 Canvas course_code 中的课程号一致 |
- 用户在受控窗口打开
https://v.sjtu.edu.cn/,完成 jAccount SSO。 - 站点在
sjtu.edu.cn域种 Cookie。 - 后续
/jy-application-resourcemanage/v1/*全部接口带 Cookie 即可,不需要jwt-token头。[实测 2026-10-06]
GET https://v.sjtu.edu.cn/cloud-rbac/authority/me成功条件:code === '0' 且 result.user 存在。返回里含学号、姓名、院系、角色(如「CANVAS学生」「CANVAS校内」)和权限 sign 列表。[实测]
-
jwt-token是resmgr门户(旁听等)的凭据,存在 localStorage;课堂录像侧不需要。 - Canvas LTI 启动页需要 Canvas 网页会话,直接调 v.sjtu 更简单。
- Canvas Personal Token 对课堂视频链路没有帮助。
接口返回空响应或空列表,不返回 401;SPA 会一直显示「加载中」。集成时必须主动调用 authority/me 探活,不要把空数据当成「无录课」。[实测]
Base:https://v.sjtu.edu.cn/jy-application-resourcemanage/v1
| 接口 | 关键参数 | 返回 |
|---|---|---|
GET https://v.sjtu.edu.cn/cloud-rbac/authority/me |
- | 登录态、学号姓名院系、角色、权限 |
GET /v1/app/info |
- | 应用注册信息、oauth 入口 |
GET /v1/list/termYear |
- | 学期列表;Dela 取 currentTerm。参数/返回结构待复核(见待验证清单) |
GET /v1/myself/curriculum |
acteId、courBeginTime、courEndTime、page.*
|
我的课程场次:teclId、subjCode/subjName、courBeginTime;含未来排课 |
GET /v1/teachingClass/{teclId}/t-1 |
teclId 路径 | 教学班详情:学年学期、院系、录课配置、学生名单 |
GET /v1/group_subject_vod_list/t-1 |
page.orders[0].field=courBeginTime、asc=false
|
备用点播课程列表;Dela 在 curriculum 为空时补查,并按 acteId 过滤 |
| 接口 | 关键参数 | 返回 |
|---|---|---|
GET /v1/subject_vod_list_new |
teclIds、page.*
|
场次列表:id、起止时间、教室 clroName、letiNumber、教师、缩略图 shotImgUrl、courTransferFlag、coursePptSliceStatus
|
GET /v1/course_vod_videoinfos |
courseId |
单场元数据 + 直播状态;不含直链 |
GET /v1/course_vod_urls_new |
courseId |
courseVodViewList:双路 MP4(url、viewNum 1/5、vodTime 秒) |
GET /v1/mss_sdk_play_mode |
courseId |
通道配置:{"name":["链路1","链路2","校内"],"show":[1,1,0]}
|
GET /v1/vod/playCount |
id |
播放次数 |
GET /v1/source/vod/lastPlayInfoByTeclId |
teclId |
最近观看场次 |
GET /v1/vod/download/{courseId} |
fileName |
官方 zip 下载 |
| 接口 | 关键参数 | 返回 |
|---|---|---|
GET /v1/course_vod_subtitle_with_keyword |
courseId |
courseTransferEsDtoList[]:bg/ed(毫秒)、res(句文本) |
GET /v1/course/ai/textSummary/{courseId} |
路径 |
mindmap:知识点树,节点带 label、timeRanges[](秒);summary:JSON 字符串 |
GET /v1/course/ai/summary/search/{courseId} |
路径 |
documentSkims[]:time(如 "7-689")、overview、content
|
GET /v1/course/ai/ppt |
courseId |
docList[]:imageUrl、ocrText、imageSeekTime(秒)、textKeywordList
|
POST /v1/aftertime/captions/{courseId} |
空 JSON |
captionsReleaseStatus 字幕发布状态 |
通用响应:
{
"code": null,
"data": { "...": "..." },
"message": "...",
"ok": true,
"status": 200
}- 成功:
ok: true,code: null;authority/me用code: '0'。 - 失败:
ok: false、dataOk: false、status非 200,或code: '-1'。unwrap时都要判。 - 列表数据在
data.records;分页字段pageCount。
分页参数:
page.pageIndex=1
page.pageSize=1000
page.orders[0].field=courBeginTime
page.orders[0].asc=false
关键事实:
-
pageIndex从 1 开始。 - 官方空分页可能返回
records: null;只有在第一页、ok:true、dataOk:true、rowCount:0、pageCount:0时才当空数组。[实测] - 后续页不能用「零总数」清空已取结果;否则网络抖动会把列表清空。
- 没有
pageCount时,用records.length < pageSize判断结束。
- 转写:中文课堂 ASR,同音字噪声明显。它适合检索,也可以作为 Agent 的参考文本,但不作为权威讲稿;展示时应标注「机器转写」。
- OCR:PPT 页文本可用,但混入播放器 UI 噪声(如「保存」「切换」「更多」)。同样标来源。
-
三套时间戳:知识点
timeRanges(秒)、转写bg/ed(毫秒)、PPTimageSeekTime(秒)。做「点击知识点跳转」时秒级精度足够。 - 元数据:课名/教师/教室/时间/节次来自排课系统,准确度较高。
-
覆盖:2026-10-06 实测 Canvas 在读 9 门课,其中 8 门有录课,计算机伦理学没有。录课取决于教室设备和课程配置,并非全覆盖。
[实测]
-
未登录返回空列表或空响应,不返回 401:先调用
authority/me;空列表不等于无录课。 -
未来排课也在列表里:
curriculum含未上的场次;用时间/lvcrVodStatus区分已录/未上。 - 转写和 PPT 是异步的:录制当天可能还没好;存状态位,下次重拉,不要假设恒可用。
- 部分课程没有录课:UI 必须支持「该课无录课」状态。
-
MP4 是签名 URL:
?key=<timestamp>-0-0-<hex>,免 Cookie、支持 Range;单次观察 ≥8h 仍有效,确切 TTL 未测出。每次播放现取,不要缓存 URL。 -
不要缓存签名 URL:只缓存场次元数据;播放/下载前重新调
course_vod_urls_new。 -
不要把播放上报当功能:
addVodWatchRecord、recordPlayInfo、keepAlive等写接口一律不调;部分课程以观看记录考核,第三方播放不应伪装成官方观看。 -
JWT 只给门户:
resmgr的jwt-token不要拿来调课堂录像 API。 - LTI 只给 Canvas 内嵌:直接调 v.sjtu 更简单,不要为了拿 LTI token 去模拟 Canvas 网页会话。
-
list/termYear的返回/参数可能变化:Dela 当前取currentTerm;学期切换时要复核,必要时回退到 teachingClass 的acteId。 - 限流未观察:单日数十次调用无异常,但不要压测;按需重试即可。
- 直播未验证:首版不涉及。
// session 是已登录 v.sjtu 的持久会话
const BASE = 'https://v.sjtu.edu.cn/jy-application-resourcemanage/v1';
async function probe(session: CampusSession) {
const response = await session.fetch('https://v.sjtu.edu.cn/cloud-rbac/authority/me', {
headers: { Accept: 'application/json' },
});
const payload = await response.json();
if (String(payload.code) !== '0' || !payload.result?.user) {
throw new Error('校园会话已失效,请登录交大视频');
}
return payload.result.user;
}
function recordsOf<T>(payload: any): T[] {
const data = payload?.data;
if (data?.records === null) {
if (
payload.ok === true &&
payload.dataOk === true &&
data.rowCount === 0 &&
data.pageCount === 0
) {
return [];
}
throw new Error('视频列表返回了空记录');
}
if (!Array.isArray(data?.records)) throw new Error('视频列表结构已变化');
return data.records as T[];
}
async function listVideos(session: CampusSession, teclId: number) {
const query = new URLSearchParams({
teclIds: String(teclId),
'page.pageIndex': '1',
'page.pageSize': '1000',
'page.orders[0].field': 'courBeginTime',
'page.orders[0].asc': 'false',
});
const response = await session.fetch(`${BASE}/subject_vod_list_new?${query}`, {
headers: { Accept: 'application/json', Referer: 'https://v.sjtu.edu.cn/jy-application-resourcemanage-ui/' },
});
const payload = await response.json();
if (payload.ok === false || payload.dataOk === false) throw new Error('视频列表请求失败');
return recordsOf<{ id: number; courBeginTime: string; clroName?: string }>(payload);
}
async function getStreams(session: CampusSession, courseId: number) {
const response = await session.fetch(`${BASE}/course_vod_urls_new?courseId=${courseId}`, {
headers: { Accept: 'application/json', Referer: 'https://v.sjtu.edu.cn/jy-application-resourcemanage-ui/' },
});
const payload = await response.json();
const streams = payload.data?.courseVodViewList ?? [];
return streams.map((stream: { url: string; viewNum: number; vodTime?: number }) => ({
url: stream.url,
view: stream.viewNum,
duration: stream.vodTime ?? 0,
}));
}本地个人播放器可以把 MP4 URL 直接交给 <video>;如果要下载,用 Range 请求并校验 206/文件大小。签名 URL 每次播放前重新取。
桌面应用建议把播放放主进程代理:签名 URL 不进 renderer,Range/Referer 由主进程转发,避免远程内容按可猜测的 videoId 盗播。Dela 的 src/main/vsjtu/playback.ts 就是这么做的(每次运行随机 HMAC token)。[实测 2026-10]
- 不要上报观看记录、伪造播放进度、把第三方播放说成官方观看。
- 不要批量下载或重新分发课堂录像;课程内容不属于公开资源。
- 不要绕过登录、破解签名、抓取他人课程。
- 不要直播/旁听门户未验证的接口。
- 四个子系统、
teclId/acteId/courseId、认证只需 Cookie、authority/me探活、空列表行为、双路 MP4、签名 TTL、转写/总结/PPT 字段、覆盖率、异步流水线:[实测 2026-10-06,内部实测记录未入库;实现见 Dela src/main/vsjtu/*] - Dela 代码中的
list/termYear、records:null处理、group_subject_vod_list/t-1备用列表、播放代理:[实测 2026-09/10,Dela src/main/vsjtu/*] - 交大视频:
[官方]https://v.sjtu.edu.cn/
- 2026-10-25:首版。基于内部实测记录与 Dela 代码重写,补充转写、AI、PPT 端点、数据质量和常见问题。
本 Wiki 记录的是开发实践,不是学校官方承诺。每个结论都标注证据与日期;校园平台会调整字段、权限和登录方式,动手前请按页面里的「最后验证」复核。发现过期内容请提 Issue 或直接改。