服务端 API
使用说明
地址
- 服务端 API 采用了 HTTP RPC-style 的命名方式;
- 接口请求地址前缀为:
https://live.edusoho.com/api-v2
; - 例
/room/get
接口,它的完整请求地址为:https://live.edusoho.com/api-v2/room/get
参数
- 数据获取类接口,采用 GET 请求,参数放置在 URL 的
query
上: 如:http://live.edusoho.com/api-v2/room/get?id=1
; - 数据更新类接口,采用 POST 请求,参数以 JSON 的方式放置在 HTTP 的请求体中;
- 时间相关参数,如未特殊说明,单位均为毫秒时间戳,例
1596707634311
。
鉴权
在接口请求 HTTP 头部中需携带 TOKEN:Authorization: Bearer TOKEN
。 使用 JWT 生成 Token。
{
"kid": "testAccessKey",
"typ": "JWT",
"alg": "HS256"
}
JWT Paylod
{
"iss": "live api",
"exp": 1605001379
}
名称 | 类型 | 描述 |
---|
iss | string | JWT的签发者,该值固定为 live api |
exp | integer | JWT 过期时间戳(秒) |
JWT Signature
使用云平台账号 SecretKey
和 HS256 算法签名。
生成 Token 的 Java 示例代码
引入java-jwt
包:
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.19.2</version>
</dependency>
Token生成示例:
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
String token = JWT.create()
.withKeyId("<your access key>")
.withIssuer("live api")
.withExpiresAt(new Date(System.currentTimeMillis() + 300000))
.sign(Algorithm.HMAC256("<your secret key>"));
响应
所有接口请求响应,均为 JSON 格式。调用成功的 API 请求,HTTP 状态码为200
;失败的请求,HTTP 状态码为 4xx
或者 5XX
。
成功响应
单个对象的响应,如:
{
"id": 1000,
"name": "测试直播课",
....
"createdAt": 1596707634311,
}
时间相关字段,如未特殊说明,单位均为毫秒时间戳,例上述的对象的 createdAt
。
多个对象的响应,分两种情况。不含分页信息,如:
[
{...},
{...},
...
{...},
]
含分页信息,如:
{
"data": [
{...},
{...},
...
{...}
],
"total": 100,
"page": 0,
"size": 20
}
data
: 响应对象列表,如无对象,则为[]
;total
: 当前查询条件下,能查询到的对象总数;page
: 当前页码,从 0
开始计数,即第一页的 page
为 0
;size
: 每页的对象数量,接口无如特殊说明,size
的最大为100
,建议使用10
、20
、30
...这样的整数,查询速度最快。
失败响应
失败响应,HTTP状态码为 4XX
或者 5XX
。同时返回 Error 结构体,如:
{
"timestamp": 1655093059709,
"status": 404,
"code": "NOT_FOUND",
"message": "课堂ID不存在",
"path": "/room/update"
}
timestamp
: 错误发生的时间戳status
: HTTP 状态码code
: 错误码message
: 错误说明path
: 接口地址
跟接口业务相关的错误码,在每个接口中说明中列出。通用错误码,如下:
INVALID_ARGUMENT
: 请求参数不正确;ACCESS_DENIED
:无权访问;UNAUTHORIZED
:未提供鉴权 Token;INVALID_AUTHENTICATION
:鉴权失败;ACCOUNT_DISABLED
:直播账号已被关闭,请联系您的商务经理。ACCOUNT_NOT_AUTHORITY
:直播账号无权访问接口,请联系您的商务经理。
API SDK
可以根据接口说明,自行调用 HTTP 接口,也可通过引入服务端 API SDK 调用接口。目前,已提供 Java SDK,建议 Java 开发的程序,使用 SDK 来调用直播课堂接口。
引入 SDK
<dependency>
<groupId>com.edusoho.live</groupId>
<artifactId>eslive-api-java-sdk</artifactId>
<version>1.0.2</version>
</dependency>
可在 https://mvnrepository.com/artifact/com.edusoho.live/eslive-api-java-sdkopen in new window 查看最新的 SDK 版本。
使用示例
import com.edusoho.live.sdk.ClientConfig;
import com.edusoho.live.sdk.EsliveApiClient;
import com.edusoho.live.sdk.model.Room;
ClientConfig config = new ClientConfig();
config.setAccessKey("<your access key>");
config.setSecretKey("< your secret key>");
EsliveApiClient client = new EsliveApiClient(config);
Room room = client.roomGet(1000L);
课堂管理
获取课堂信息
参数
响应
{
"id": 1,
"name": "测试课堂",
"startAt": 1597041185627,
"endAt": 1597127585627,
"actualStartAt": 0,
"actualEndAt": 0,
"status": "unstart",
"isDeleted": false,
"isReplayGenerated": false,
"userTotalNum": 300,
"userEnterTotalNum": 445,
"maxOnlineNum": 200,
"chatTotalNum": 356,
"userAvgOnlineDuration": 2234523,
"createdAt": 1596707634311,
"updatedAt": 1596707634311
}
名称 | 类型 | 描述 |
---|
id | integer | 课堂ID |
name | string | 课堂名称 |
startAt | integer | 课堂开始时间 |
endAt | integer | 课堂结束时间 |
actualStartAt | integer | 直播间实际开始时间 |
actualEndAt | integer | 直播间实际结束时间 |
status | string | 直播间状态 (未开始:unstart , 直播中:living , 暂停:pause , 取消:canceled , 结束:finished ) |
isDeleted | boolean | 是否删除 |
isReplayGenerated | boolean | 回放是否生成 |
userTotalNum | integer | 累计在线人数 |
userEnterTotalNum | integer | 累计在线人次 |
maxOnlineNum | integer | 最大在线人数 |
chatTotalNum | integer | 总聊天数 |
userAvgOnlineDuration | integer | 用户平均在线时长 |
createdAt | integer | 课堂创建时间 |
updatedAt | integer | 课堂更新时间 |
错误码
创建课堂
参数
名称 | 类型 | 描述 |
---|
name | string | 课堂名称 (最长200个字符) |
startAt | integer | 课堂开始时间 (开始时间不能早于当前时间) |
endAt | integer | 课堂结束时间 (课堂总时长不能大于 8 小时) |
groupBundleNo 可选 | integer | 分组大组编号 |
错误码
BUNDLE_NOT_FOUND
: 分组大组不存在
响应
{
"id": 1,
"name": "测试课堂",
"startAt": 1597041185627,
"endAt": 1597127585627,
"actualStartAt": 0,
"actualEndAt": 0,
"status": "unstart",
"isDeleted": false,
"isReplayGenerated": false,
"userTotalNum": 300,
"userEnterTotalNum": 445,
"maxOnlineNum": 200,
"chatTotalNum": 356,
"userAvgOnlineDuration": 2234523,
"createdAt": 1596707634311,
"updatedAt": 1596707634311
}
名称 | 类型 | 描述 |
---|
id | integer | 课堂ID |
name | string | 课堂名称 |
startAt | integer | 课堂开始时间 |
endAt | integer | 课堂结束时间 |
actualStartAt | integer | 直播间实际开始时间 |
actualEndAt | integer | 直播间实际结束时间 |
status | string | 直播间状态 (未开始:unstart , 直播中:living , 暂停:pause , 取消:canceled , 结束:finished ) |
isDeleted | boolean | 是否删除 |
isReplayGenerated | boolean | 回放是否生成 |
userTotalNum | integer | 累计在线人数 |
userEnterTotalNum | integer | 累计在线人次 |
maxOnlineNum | integer | 最大在线人数 |
chatTotalNum | integer | 总聊天数 |
userAvgOnlineDuration | integer | 用户平均在线时长 |
createdAt | integer | 课堂创建时间 |
updatedAt | integer | 课堂更新时间 |
更新课堂信息
参数
名称 | 类型 | 描述 |
---|
id | integer | 课堂ID |
name 可选 | string | 课堂名称 |
startAt 可选 | integer | 课堂开始时间 |
endAt 可选 | integer | 课堂结束时间 |
响应
{
"id": 1,
"name": "测试课堂",
"startAt": 1597041185627,
"endAt": 1597127585627,
"actualStartAt": 0,
"actualEndAt": 0,
"status": "unstart",
"isDeleted": false,
"isReplayGenerated": false,
"userTotalNum": 300,
"userEnterTotalNum": 445,
"maxOnlineNum": 200,
"chatTotalNum": 356,
"userAvgOnlineDuration": 2234523,
"createdAt": 1596707634311,
"updatedAt": 1596707634311
}
名称 | 类型 | 描述 |
---|
id | integer | 课堂ID |
name | string | 课堂名称 |
startAt | integer | 课堂开始时间 |
endAt | integer | 课堂结束时间 |
actualStartAt | integer | 直播间实际开始时间 |
actualEndAt | integer | 直播间实际结束时间 |
status | string | 直播间状态 (未开始:unstart , 直播中:living , 暂停:pause , 取消:canceled , 结束:finished ) |
isDeleted | boolean | 是否删除 |
isReplayGenerated | boolean | 回放是否生成 |
userTotalNum | integer | 累计在线人数 |
userEnterTotalNum | integer | 累计在线人次 |
maxOnlineNum | integer | 最大在线人数 |
chatTotalNum | integer | 总聊天数 |
userAvgOnlineDuration | integer | 用户平均在线时长 |
createdAt | integer | 课堂创建时间 |
updatedAt | integer | 课堂更新时间 |
错误码
关闭课堂
参数
响应
错误码
删除课堂
参数
响应
错误码
课堂回放
获取回放信息
参数
响应
{
"roomId": 1,
"roomName": "测试课",
"duration": 223423,
"status": "finished",
"createdAt": 1596707634311,
"updatedAt": 1596707634311
}
名称 | 类型 | 描述 |
---|
roomId | integer | 课堂ID |
roomName | string | 课堂名称 |
duration | integer | 回放时长(毫秒) |
status | string | 回放录制状态 (未录制:unstart , 录制中:generating , 录制成功:finished ,没有回放:none ,录制失败:error ) |
createdAt | integer | 创建时间(毫秒) |
updatedAt | integer | 更新时间(毫秒) |
错误码
REPLAY_NOT_FOUND
: 回放不存在。当直播尚未开始,调用此接口,报此错误。
批量获取回放信息
参数
响应
[
{
"roomId": 1,
"roomName": "测试课1",
"duration": 223423,
"status": "finished",
"createdAt": 1596707634311,
"updatedAt": 1596707634311
},
{
"roomId": 2,
"roomName": "测试课2",
"duration": 223423,
"status": "finished",
"createdAt": 1596707634311,
"updatedAt": 1596707634311
},
...
]
名称 | 类型 | 描述 |
---|
roomId | integer | 课堂ID |
roomName | string | 课堂名称 |
duration | integer | 回放时长(毫秒) |
status | string | 回放录制状态 (未录制:unstart , 录制中:generating , 录制成功:finished , 没有回放: none , 录制失败:error ) |
createdAt | integer | 创建时间(毫秒) |
updatedAt | integer | 更新时间(毫秒) |
删除回放
参数
响应
错误码
REPLAY_NOT_FOUND
: 回放不存在。当直播尚未开始,调用此接口,报此错误。
课堂成员
获取成员列表
参数
名称 | 类型 | 描述 |
---|
roomId | integer | 课堂 ID |
page | integer | 页码 (从0 开始) |
size | integer | 每页数量(最大1000) |
响应
{
"data": [
{
"roomId": 1000,
"userId": 10001,
"userName": "student01",
"role": "viewer",
"firstEnterAt": 1612511578769,
"lastEnterAt": 1712511578769,
"lastLeaveAt": 1712516578769,
"firstIp": "127.0.0.1",
"lastIp": "127.0.0.2",
"checkinNum": 2,
"answerNum": 2,
"chatNum": 2,
"onlineDuration": 15323,
"watchDuration": 12000
}
],
"total": 1,
"page": 0,
"size": 20
}
名称 | 类型 | 描述 |
---|
roomId | integer | 课堂 ID |
userId | integer | 用户 ID |
userName | string | 用户名 |
role | enum | 角色(主讲人/老师:speaker ,助教:assistant ,学生:viewer ,技术支持:supporter ) |
firstEnterAt | integer | 首次进入时间戳(毫秒) |
lastEnterAt | integer | 最新进入时间戳(毫秒) |
lastLeaveAt | integer | 最新离开时间戳(毫秒) |
firstIp | string | 首次进入IP |
lastIp | string | 最新进入IP |
checkinNum | integer | 签到次数 |
answerNum | integer | 答题次数 |
chatNum | integer | 聊天次数 |
onlineDuration | integer | 累计在线时长 |
watchDuration | integer | 累计推流/观看时长 |
获取成员访问记录
参数
名称 | 类型 | 描述 |
---|
roomId | integer | 课堂 ID |
page | integer | 页码 (从0 开始) |
size | integer | 每页数量(最大1000) |
响应
{
"data": [
{
"userId": 216582789918825,
"userName": "学员001",
"role": "viewer",
"enterAt": 1611917258711,
"leaveAt": 1611917458711
}
],
"total": 1,
"page": 0,
"size": 20
}
课堂分组
bundle
指分组大组,group
指分组,group 从属于 bundle; 大组会关联到相应的直播课堂,当分组有变更时,所有关联的课堂都会变更
创建分组大组
创建直播间前,查询本地数据库,如果之前还没有创建 bundle,则调用此接口。
POST /memberGroup/createBundle
参数
响应
{
"no": 22,
"name": "大组1",
"createdAt": 1612511578769,
"updatedAt": 1612511578769
}
参数说明:
名称 | 类型 | 描述 |
---|
no | integer | 大组编号 |
name | string | 大组名称 |
createdAt | integer | 创建时间 |
updatedAt | integer | 更新时间 |
批量创建分组
POST /memberGroup/batchCreate
参数
名称 | 类型 | 描述 |
---|
bundleNo | integer | 大组编号 |
names | arraystring | 分组名称 |
响应
[
{
"no": "1687e375a82c47b1a062f4f8eafe44cc",
"name": "分组1"
},
{
"no": "1687e375a82c47b1a062f4f8eafe44bb",
"name": "分组2"
}
]
参数说明:
名称 | 类型 | 描述 |
---|
no | string | 分组编号 |
name | integer | 分组名称 |
错误码
code | message | 描述 |
---|
BUNDLE_NOT_FOUND | 大组不存在 | bundleNo 对应的大组不存在 |
INVALID_ARGUMENT | 分组不能超过 100 个 | 分组不能超过 100 个 |
创建单个分组
参数
名称 | 类型 | 描述 |
---|
bundleNo | integer | 大组编号 |
name | string | 分组名称 |
响应
{
"no": "1687e375a82c47b1a062f4f8eafe44cc",
"name": "6124567"
}
参数说明:
名称 | 类型 | 描述 |
---|
no | string | 分组编号 |
name | string | 分组名称 |
错误码
code | message | 描述 |
---|
BUNDLE_NOT_FOUND | 大组不存在 | bundleNo 对应的大组不存在 |
INVALID_ARGUMENT | 分组不能超过 100 个 | 分组不能超过 100 个 |
更新分组名称
POST /memberGroup/updateName
参数
名称 | 类型 | 描述 |
---|
no | string | 分组编号 |
name | string | 分组名称 |
响应
{
"no": "1687e375a82c47b1a062f4f8eafe44cc",
"name": "6124567"
}
参数说明:
名称 | 类型 | 描述 |
---|
no | string | 分组编号 |
name | integer | 分组名称 |
错误码
code | message | 描述 |
---|
GROUP_NOT_FOUND | 分组不存在 | groupNo 对应的分组不存在 |
批量删除分组
POST /memberGroup/batchDelete
参数
响应
错误码
code | message | 描述 |
---|
GROUP_NOT_FOUND | 分组不存在 | 分组不存在 |
GROUP_DELETED | 分组已删除 | 分组已删除 |
删除分组
参数
响应
错误码
code | message | 描述 |
---|
GROUP_NOT_FOUND | 分组不存在 | 分组不存在 |
GROUP_DELETED | 分组已删除 | 分组已删除 |
课堂签到
获取直播课堂签到记录
参数
名称 | 类型 | 描述 |
---|
roomId | integer | 课堂 ID |
page | integer | 页码 (从0 开始) |
size | integer | 每页数量(最大1000) |
响应
{
"data": [
{
"id": 13,
"roomId": 20536,
"checkInNum": 1,
"onlineNum": 2,
"startAt": 1611558178976,
"endAt": 1611558210606,
"duration": 30
},
{
"id": 14,
"roomId": 20536,
"checkInNum": 1,
"onlineNum": 2,
"startAt": 1611558304933,
"endAt": 1611558312157,
"duration": 30
}
],
"total": 2,
"page": 0,
"size": 20
}
名称 | 类型 | 描述 |
---|
id | integer | 签到 ID |
roomId | integer | 直播间 ID |
checkInNum | integer | 签到人数 |
onlineNum | integer | 当前在线人数 |
startAt | integer | 开始时间 |
endAt | integer | 结束时间 |
duration | integer | 签到时长(秒) |
课堂答题
获取直播课堂答题记录
参数
名称 | 类型 | 描述 |
---|
roomId | integer | 课堂 ID |
page | integer | 页码 (从0 开始) |
size | integer | 每页数量(最大1000) |
响应
{
"data": [
{
"id": 104,
"roomId": 20775,
"contentId": 343,
"contentName": "第1章 绪论 (1).pdf",
"page": 1,
"optionNum": 3,
"rightAnswer": "[1]",
"answerUserNum": 0,
"rightUserNum": 0,
"onlineNum": 1,
"startAt": 1612511578769,
"endAt": 1612511588970,
"duration": 10201
},
{
"id": 105,
"roomId": 20775,
"contentId": 343,
"contentName": "第1章 绪论 (1).pdf",
"page": 1,
"optionNum": 8,
"rightAnswer": "[1,4,5]",
"answerUserNum": 0,
"rightUserNum": 0,
"onlineNum": 1,
"startAt": 1612511604175,
"endAt": 1612511619783,
"duration": 15608
}
],
"total": 2,
"page": 0,
"size": 20
}
名称 | 类型 | 描述 |
---|
id | integer | 答题 ID |
roomId | integer | 直播间 ID |
contentId | integer | 课件 ID |
contentName | string | 课件名称 |
page | integer | 课件页码 |
optionNum | integer | 选项个数 |
rightAnswer | string | 正确答案 |
answerUserNum | integer | 提交回答人数 |
rightUserNum | integer | 正确人数 |
onlineNum | integer | 在线人数 |
startAt | integer | 开始时间 |
endAt | integer | 结束时间 |
duration | integer | 答题时长 |
课堂聊天记录
获取直播课堂聊天记录
参数
名称 | 类型 | 描述 |
---|
roomId | integer | 课堂 ID |
page | integer | 页码 (从0 开始) |
size | integer | 每页数量(最大1000) |
响应
{
"data": [
{
"id": 105,
"roomId": 20775,
"userId": 343,
"userName": "阿三",
"role": "viewer",
"message": "喂喂喂",
"isDeleted": 0,
"isSensitive": 0,
"isCombo": 0,
"type": "txt",
"createdAt": 1612511619783
}
],
"total": 2,
"page": 0,
"size": 20
}
名称 | 类型 | 描述 |
---|
id | integer | 答题 ID |
roomId | integer | 直播间 ID |
userId | integer | 用户 ID |
userName | string | 用户名 |
role | string | 用户角色 speaker: 主播,viewer: 观众,supporter:技术支持;assistant:助教, vip: 嘉宾 |
message | string | 消息内容 |
isDeleted | integer | 是否删除 |
isSensitive | integer | 是否敏感词 |
isCombo | integer | 是否连击词 |
type | string | 类型 txt文本;img图片;reply:回复;action:互动;question:问答 |
createdAt | integer | 消息时间戳 |
事件回调
- 往
webhookUrl
POST 数据。 - 收到 webhook 请求后,业务方需回应,HTTP 200 状态码,HTTP Body 需为
ok
,否则会认为回调失败,会重复通知。 - 发送失败后,最多重试发送2次,业务方需做好逻辑处理,可能会收到多次重复的通知。
- 会在 HTTP 头部添加
Authorization: Bearer <token>
,业务方需鉴权。
鉴权
<token>
为 JWT。
JWT HEADER:
{
"typ": "JWT",
"alg": "HS256"
}
JWT Paylod:
{
"iss": "live webhook",
"exp": 1605001379
}
参数说明:
名称 | 类型 | 描述 |
---|
iss | string | JWT的签发者,该值固定为 live webhook |
exp | integer | JWT 过期时间戳(秒) |
JWT Signature:
使用云平台账号 SecretKey
和 HS256 算法签名。
事件消息
开始上课 room.started
{
"event": "room.started",
"time": 1608776100111,
"room": {
"id": 20001,
"name": "测试",
"actualStartAt": 1608776100000,
"actualEndAt": 0,
}
}
名称 | 类型 | 描述 |
---|
event | string | 事件名称 |
time | Long | 事件发生时间(毫秒) |
room.id | Long | 直播课堂ID |
room.name | String | 直播课堂名称 |
room.actualStartAt | Long | 直播课堂实际开始时间 |
room.actualEndAt | Long | 直播课堂实际结束时间,未结束时值为0 |
结束下课 room.finished
{
"event": "room.finished",
"time": 1608776100111,
"room": {
"id": 20001,
"name": "测试",
"actualStartAt": 1608776100000,
"actualEndAt": 1608876100000,
}
}
回放录制成功 replay.generated
{
"event": "replay.generated",
"time": 1608776100111,
"room": {
"id": 20001,
"name": "测试",
"actualStartAt": 1608776100000,
"actualEndAt": 1608876100000,
}
}
回放录制失败 replay.error
{
"event": "replay.error",
"time": 1608776100111,
"room": {
"id": 20001,
"name": "测试",
"actualStartAt": 1608776100000,
"actualEndAt": 1608876100000,
}
}