服务端 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。

JWT HEADER

{
  "kid": "testAccessKey",
  "typ": "JWT",
  "alg": "HS256"
}
  • kid: 即云平台账号的 AccessKey

JWT Paylod

{
  "iss": "live api",
  "exp": 1605001379
}
名称类型描述
issstringJWT的签发者,该值固定为 live api
expintegerJWT 过期时间戳(秒)

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)) //5分钟过期时间
        .sign(Algorithm.HMAC256("<your secret key>"));

TIP

Java 开发的程序,建议使用 eslive-api-java-sdk SDK 包,SDK 封装了此 Token 的计算逻辑。

响应

所有接口请求响应,均为 JSON 格式。调用成功的 API 请求,HTTP 状态码为200;失败的请求,HTTP 状态码为 4xx 或者 5XX

成功响应

单个对象的响应,如:

{
  "id": 1000,
  "name": "测试直播课",
  ....
  "createdAt": 1596707634311,
}

时间相关字段,如未特殊说明,单位均为毫秒时间戳,例上述的对象的 createdAt

多个对象的响应,分两种情况。不含分页信息,如:

[
  {...}, // 对象 1
  {...}, // 对象 2
  ...
  {...}, // 对象 N
]

含分页信息,如:

{
    "data": [
        {...}, // 对象 1
        {...}, // 对象 2
        ...
        {...} // 对象 N
    ],
    "total": 100,
    "page": 0,
    "size": 20
}
  • data: 响应对象列表,如无对象,则为[]
  • total: 当前查询条件下,能查询到的对象总数;
  • page: 当前页码,从 0 开始计数,即第一页的 page0
  • size: 每页的对象数量,接口无如特殊说明,size的最大为100,建议使用102030...这样的整数,查询速度最快。

失败响应

失败响应,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);

// Client 中的接口方法名与接口说明中的接口地址一一对应
// 例如接口地址为 /room/get ,那么在 Client 中的方法名为 roomGet
Room room = client.roomGet(1000L); // 获取课堂ID为 1000 的课堂信息

课堂管理

获取课堂信息

GET /room/get

参数

名称类型描述
idinteger课堂ID

响应

{
  "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
}
名称类型描述
idinteger课堂ID
namestring课堂名称
startAtinteger课堂开始时间
endAtinteger课堂结束时间
actualStartAtinteger直播间实际开始时间
actualEndAtinteger直播间实际结束时间
statusstring直播间状态
(未开始:unstart, 直播中:living, 暂停:pause, 取消:canceled, 结束:finished
isDeletedboolean是否删除
isReplayGeneratedboolean回放是否生成
userTotalNuminteger累计在线人数
userEnterTotalNuminteger累计在线人次
maxOnlineNuminteger最大在线人数
chatTotalNuminteger总聊天数
userAvgOnlineDurationinteger用户平均在线时长
createdAtinteger课堂创建时间
updatedAtinteger课堂更新时间

错误码

  • ROOM_NOT_FOUND: 直播课堂不存在。

创建课堂

POST /room/create

参数

名称类型描述
namestring课堂名称 (最长200个字符)
startAtinteger课堂开始时间 (开始时间不能早于当前时间)
endAtinteger课堂结束时间 (课堂总时长不能大于 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
}
名称类型描述
idinteger课堂ID
namestring课堂名称
startAtinteger课堂开始时间
endAtinteger课堂结束时间
actualStartAtinteger直播间实际开始时间
actualEndAtinteger直播间实际结束时间
statusstring直播间状态
(未开始:unstart, 直播中:living, 暂停:pause, 取消:canceled, 结束:finished
isDeletedboolean是否删除
isReplayGeneratedboolean回放是否生成
userTotalNuminteger累计在线人数
userEnterTotalNuminteger累计在线人次
maxOnlineNuminteger最大在线人数
chatTotalNuminteger总聊天数
userAvgOnlineDurationinteger用户平均在线时长
createdAtinteger课堂创建时间
updatedAtinteger课堂更新时间

更新课堂信息

POST /room/update

参数

名称类型描述
idinteger课堂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
}
名称类型描述
idinteger课堂ID
namestring课堂名称
startAtinteger课堂开始时间
endAtinteger课堂结束时间
actualStartAtinteger直播间实际开始时间
actualEndAtinteger直播间实际结束时间
statusstring直播间状态
(未开始:unstart, 直播中:living, 暂停:pause, 取消:canceled, 结束:finished
isDeletedboolean是否删除
isReplayGeneratedboolean回放是否生成
userTotalNuminteger累计在线人数
userEnterTotalNuminteger累计在线人次
maxOnlineNuminteger最大在线人数
chatTotalNuminteger总聊天数
userAvgOnlineDurationinteger用户平均在线时长
createdAtinteger课堂创建时间
updatedAtinteger课堂更新时间

错误码

  • ROOM_NOT_FOUND: 直播课堂不存在。

关闭课堂

POST /room/close

参数

名称类型描述
idinteger课堂ID

响应

{
  "ok": true
}

错误码

  • ROOM_NOT_FOUND: 直播课堂不存在。

删除课堂

POST /room/delete

参数

名称类型描述
idinteger课堂ID

响应

{
  "ok": true
}

错误码

  • ROOM_NOT_FOUND: 直播课堂不存在。

课堂回放

获取回放信息

GET /replay/get

参数

名称类型描述
roomIdinteger课堂ID

响应

{
  "roomId": 1,
  "roomName": "测试课",
  "duration": 223423,
  "status": "finished",
  "createdAt": 1596707634311,
  "updatedAt": 1596707634311
}
名称类型描述
roomIdinteger课堂ID
roomNamestring课堂名称
durationinteger回放时长(毫秒)
statusstring回放录制状态
(未录制:unstart, 录制中:generating, 录制成功:finished,没有回放:none,录制失败:error)
createdAtinteger创建时间(毫秒)
updatedAtinteger更新时间(毫秒)

错误码

  • REPLAY_NOT_FOUND: 回放不存在。当直播尚未开始,调用此接口,报此错误。

批量获取回放信息

GET /replay/gets

参数

名称类型描述
roomIdsarray课堂 IDs

响应

[
  {
    "roomId": 1,
    "roomName": "测试课1",
    "duration": 223423,
    "status": "finished",
    "createdAt": 1596707634311,
    "updatedAt": 1596707634311
  },
  {
    "roomId": 2,
    "roomName": "测试课2",
    "duration": 223423,
    "status": "finished",
    "createdAt": 1596707634311,
    "updatedAt": 1596707634311
  },
  ...
]
名称类型描述
roomIdinteger课堂ID
roomNamestring课堂名称
durationinteger回放时长(毫秒)
statusstring回放录制状态
(未录制:unstart, 录制中:generating, 录制成功:finished, 没有回放: none, 录制失败:error)
createdAtinteger创建时间(毫秒)
updatedAtinteger更新时间(毫秒)

删除回放

POST /replay/delete

参数

名称类型描述
roomIdinteger课堂 ID

响应

{
  "ok": true
}

错误码

  • REPLAY_NOT_FOUND: 回放不存在。当直播尚未开始,调用此接口,报此错误。

课堂成员

获取成员列表

GET /member/list

参数

名称类型描述
roomIdinteger课堂 ID
pageinteger页码 (从0开始)
sizeinteger每页数量(最大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
}
名称类型描述
roomIdinteger课堂 ID
userIdinteger用户 ID
userNamestring用户名
roleenum角色(主讲人/老师:speaker,助教:assistant,学生:viewer,技术支持:supporter)
firstEnterAtinteger首次进入时间戳(毫秒)
lastEnterAtinteger最新进入时间戳(毫秒)
lastLeaveAtinteger最新离开时间戳(毫秒)
firstIpstring首次进入IP
lastIpstring最新进入IP
checkinNuminteger签到次数
answerNuminteger答题次数
chatNuminteger聊天次数
onlineDurationinteger累计在线时长
watchDurationinteger累计推流/观看时长

获取成员访问记录

GET /member/listVisits

参数

名称类型描述
roomIdinteger课堂 ID
pageinteger页码 (从0开始)
sizeinteger每页数量(最大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

参数

名称类型描述
namestring大组名称

响应

{
  "no": 22,  
  "name": "大组1",
  "createdAt": 1612511578769,
  "updatedAt": 1612511578769
}

参数说明:

名称类型描述
nointeger大组编号
namestring大组名称
createdAtinteger创建时间
updatedAtinteger更新时间

批量创建分组

POST /memberGroup/batchCreate

参数

名称类型描述
bundleNointeger大组编号
namesarraystring分组名称

响应

[
  {
    "no": "1687e375a82c47b1a062f4f8eafe44cc",
    "name": "分组1"
  },
  {
    "no": "1687e375a82c47b1a062f4f8eafe44bb",
    "name": "分组2"
  }
]

参数说明:

名称类型描述
nostring分组编号
nameinteger分组名称

错误码

codemessage描述
BUNDLE_NOT_FOUND大组不存在bundleNo 对应的大组不存在
INVALID_ARGUMENT分组不能超过 100 个分组不能超过 100 个

创建单个分组

POST /memberGroup/create

参数

名称类型描述
bundleNointeger大组编号
namestring分组名称

响应

{
  "no": "1687e375a82c47b1a062f4f8eafe44cc",
  "name": "6124567"
}

参数说明:

名称类型描述
nostring分组编号
namestring分组名称

错误码

codemessage描述
BUNDLE_NOT_FOUND大组不存在bundleNo 对应的大组不存在
INVALID_ARGUMENT分组不能超过 100 个分组不能超过 100 个

更新分组名称

POST /memberGroup/updateName

参数

名称类型描述
nostring分组编号
namestring分组名称

响应

{
  "no": "1687e375a82c47b1a062f4f8eafe44cc",
  "name": "6124567"
}

参数说明:

名称类型描述
nostring分组编号
nameinteger分组名称

错误码

codemessage描述
GROUP_NOT_FOUND分组不存在groupNo 对应的分组不存在

批量删除分组

POST /memberGroup/batchDelete

参数

名称类型描述
nosarraystring分组编号

响应

{
  "ok": true
}

错误码

codemessage描述
GROUP_NOT_FOUND分组不存在分组不存在
GROUP_DELETED分组已删除分组已删除

删除分组

POST /memberGroup/delete

参数

名称类型描述
nostring分组编号

响应

{
  "ok": true
}

错误码

codemessage描述
GROUP_NOT_FOUND分组不存在分组不存在
GROUP_DELETED分组已删除分组已删除

课堂签到

获取直播课堂签到记录

GET /checkin/list

参数

名称类型描述
roomIdinteger课堂 ID
pageinteger页码 (从0开始)
sizeinteger每页数量(最大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
}
名称类型描述
idinteger签到 ID
roomIdinteger直播间 ID
checkInNuminteger签到人数
onlineNuminteger当前在线人数
startAtinteger开始时间
endAtinteger结束时间
durationinteger签到时长(秒)

课堂答题

获取直播课堂答题记录

GET /question/list

参数

名称类型描述
roomIdinteger课堂 ID
pageinteger页码 (从0开始)
sizeinteger每页数量(最大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
}
名称类型描述
idinteger答题 ID
roomIdinteger直播间 ID
contentIdinteger课件 ID
contentNamestring课件名称
pageinteger课件页码
optionNuminteger选项个数
rightAnswerstring正确答案
answerUserNuminteger提交回答人数
rightUserNuminteger正确人数
onlineNuminteger在线人数
startAtinteger开始时间
endAtinteger结束时间
durationinteger答题时长

课堂聊天记录

获取直播课堂聊天记录

GET /chat/list

参数

名称类型描述
roomIdinteger课堂 ID
pageinteger页码 (从0开始)
sizeinteger每页数量(最大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
}
名称类型描述
idinteger答题 ID
roomIdinteger直播间 ID
userIdinteger用户 ID
userNamestring用户名
rolestring用户角色 speaker: 主播,viewer: 观众,supporter:技术支持;assistant:助教, vip: 嘉宾
messagestring消息内容
isDeletedinteger是否删除
isSensitiveinteger是否敏感词
isCombointeger是否连击词
typestring类型 txt文本;img图片;reply:回复;action:互动;question:问答
createdAtinteger消息时间戳

事件回调

  • 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
}

参数说明:

名称类型描述
issstringJWT的签发者,该值固定为 live webhook
expintegerJWT 过期时间戳(秒)

JWT Signature:

使用云平台账号 SecretKey 和 HS256 算法签名。

事件消息

开始上课 room.started

{
    "event": "room.started",
    "time": 1608776100111,
    "room": {
        "id": 20001,
        "name": "测试",
        "actualStartAt": 1608776100000,
        "actualEndAt": 0,
    }
}
名称类型描述
eventstring事件名称
timeLong事件发生时间(毫秒)
room.idLong直播课堂ID
room.nameString直播课堂名称
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,
    }
}