diff --git a/src/main/java/com/qiniu/pili/PiliDomainManager.java b/src/main/java/com/qiniu/pili/PiliDomainManager.java
new file mode 100644
index 00000000..ed0e6b9c
--- /dev/null
+++ b/src/main/java/com/qiniu/pili/PiliDomainManager.java
@@ -0,0 +1,195 @@
+package com.qiniu.pili;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
+
+import com.qiniu.common.Constants;
+import com.qiniu.common.QiniuException;
+import com.qiniu.http.Client;
+import com.qiniu.http.Response;
+import com.qiniu.util.Auth;
+import com.qiniu.util.Json;
+
+public class PiliDomainManager extends PiliManager {
+ public PiliDomainManager(Auth auth) {
+ super(auth);
+ }
+
+ public PiliDomainManager(Auth auth, String server) {
+ super(auth, server);
+ }
+
+ public PiliDomainManager(Auth auth, String server, Client client) {
+ super(auth, server, client);
+ }
+
+ public PiliDomainManager(Auth auth, String server, Client client, String apihost, String apihttpscheme,
+ String appname) {
+ super(auth, server, client, apihost, apihttpscheme, appname);
+ }
+
+ /**
+ * 返回目标直播空间下的所有直播域名
+ * 参考文档:直播空间下的所有域名
+ * GET /v2/hubs//domains
+ *
+ * @param hub 直播空间
+ * @return 获取直播空间域名列表请求的回复
+ * @throws QiniuException 异常
+ */
+ public PiliDomainModel.DomainsListResult getDomainsList(String hub) throws QiniuException {
+ String url = super.server + "/v2/hubs/" + hub + "/domains";
+ Response response = get(url);
+ return response.jsonToObject(PiliDomainModel.DomainsListResult.class);
+ }
+
+ /**
+ *
+ * GetDomainInfo 查询域名信息
+ *
+ * @param hub 直播空间
+ *
+ * @param domain 域名
+ *
+ * @return 获取域名信息请求的回复
+ *
+ * @throws QiniuException
+ */
+ public PiliDomainModel.DomainInfoResult getDomainInfo(String hub, String domain) throws QiniuException {
+ if (Objects.isNull(hub) || Objects.isNull(domain) || hub.isEmpty() || domain.isEmpty()) {
+ throw new IllegalArgumentException("hub: " + hub + ", domain: " + domain + ", cannot be null or empty!");
+ }
+
+ String url = super.server + "/v2/hubs/" + hub + "/domains/" + domain;
+ Response response = get(url);
+ return response.jsonToObject(PiliDomainModel.DomainInfoResult.class);
+ }
+
+ /**
+ * BindDomain 绑定直播域名
+ * 参考文档:参考文档:绑定直播域名
+ * POST /v2/hubs//newdomains
+ *
+ * @param hub 直播空间
+ * @param domain 域名
+ * @param type 域名的类型
+ * @return 是否绑定成功
+ * @throws QiniuException 异常
+ */
+ public QiniuException bindDomain(String hub, String domain, String type) throws QiniuException {
+ if (type.equals(PiliDomainModel.PUBLISH_RTMP) || type.equals(PiliDomainModel.LIVE_HDL)
+ || type.equals(PiliDomainModel.LIVE_HLS) || type.equals(PiliDomainModel.PUBLISH_RTMP)) {
+ // 是其中一个
+ HashMap req = new HashMap<>();
+ req.put("hub", hub);
+ req.put("domain", domain);
+ req.put("type", type);
+ byte[] body = Json.encode(req).getBytes(Constants.UTF_8);
+ String url = server + "/v2/hubs/" + hub + "/newdomains";
+ Response response = post(url, body);
+ return response.isOK() ? null : new QiniuException(response);
+ } else {
+ // 不是
+ return new QiniuException(null, "Type Error!");
+ }
+ }
+
+ /**
+ * UnbindDomain 解绑直播域名
+ * 参考文档:参考文档:解绑直播域名
+ * DELETE /v2/hubs//domains/
+ *
+ * @param hub 直播空间
+ * @param domain 域名
+ * @return 是否解绑成功
+ * @throws QiniuException 异常
+ */
+ public QiniuException unbindDomain(String hub, String domain) throws QiniuException {
+ HashMap req = new HashMap<>();
+ req.put("hub", hub);
+ req.put("domain", domain);
+ byte[] body = Json.encode(req).getBytes(Constants.UTF_8);
+ String url = server + "/v2/hubs/" + hub + "/domains/" + domain;
+ Response response = delete(url);
+ return response.isOK() ? null : new QiniuException(response);
+ }
+
+ /**
+ * BindVodDomain 绑定点播域名
+ * 参考文档:参考文档:绑定点播域名
+ * POST /v2/hubs//voddomain
+ *
+ * @param hub 直播空间
+ * @param vodDomain 点播域名
+ * @return 是否绑定成功
+ * @throws QiniuException 异常
+ */
+ public QiniuException bindVodDomain(String hub, String vodDomain) throws QiniuException {
+ HashMap req = new HashMap<>();
+ req.put("hub", hub);
+ req.put("vodDomain", vodDomain);
+ String url = server + "/v2/hubs/" + hub + "/voddomain";
+ byte[] body = Json.encode(req).getBytes(Constants.UTF_8);
+ Response response = post(url, body);
+ return response.isOK() ? null : new QiniuException(response);
+ }
+
+ /**
+ * SetDomainCert 修改域名证书配置
+ * 参考文档:参考文档:绑定点播域名
+ * POST /v2/hubs//domains//cert
+ *
+ * @param hub 直播空间
+ * @param domain 域名
+ * @param certName 证书名称
+ * @return 是否绑定成功
+ * @throws QiniuException 异常
+ */
+ public QiniuException setDomainCert(String hub, String domain, String certName) throws Exception {
+ HashMap req = new HashMap<>();
+ req.put("hub", hub);
+ req.put("domain", domain);
+ req.put("certName", certName);
+ String url = server + "/v2/hubs/" + hub + "/domains/" + domain + "/cert";
+ byte[] body = Json.encode(req).getBytes(Constants.UTF_8);
+ Response response = post(url, body);
+ return response.isOK() ? null : new QiniuException(response);
+ }
+
+ /**
+ * SetDomainURLRewrite 修改域名改写规则配置
+ * POST /v2/hubs//domains//urlrewrite
+ *
+ * 可根据业务需求自定义推拉流URL
+ * 改写后的URL应符合七牛的直播URL规范: :////[.]?
+ * 举例
+ * 匹配规则: (.+)/live/(.+)/playlist.m3u8
+ * 改写规则: ${1}/hub/${2}.m3u8
+ * 请求URL: https://live.qiniu.com/live/stream01/playlist.m3u8 ; 改写URL:
+ * https://live.qiniu.com/hub/stream01.m3u8
+ * 请求URL: https://live.qiniu.com/live/stream01.m3u8 ; 与规则不匹配,不做改写
+ *
+ * @param hub 直播空间
+ * @param domain 域名
+ * @param rules 规则
+ * @return 是否修改成功
+ * @throws QiniuException
+ */
+ public QiniuException setDomainURLRewrite(String hub, String domain, List rules)
+ throws Exception {
+ HashMap req = new HashMap<>();
+ req.put("rules", rules);
+ req.put("hub", hub);
+ req.put("domain", domain);
+ String url = server + "/v2/hubs/" + hub + "/domains/" + domain + "/urlrewrite";
+ byte[] body = Json.encode(req).getBytes(Constants.UTF_8);
+ Response response = post(url, body);
+ return response.isOK() ? null : new QiniuException(response);
+ }
+}
diff --git a/src/main/java/com/qiniu/pili/PiliDomainModel.java b/src/main/java/com/qiniu/pili/PiliDomainModel.java
new file mode 100644
index 00000000..ae22ced5
--- /dev/null
+++ b/src/main/java/com/qiniu/pili/PiliDomainModel.java
@@ -0,0 +1,321 @@
+package com.qiniu.pili;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 这里定义了Pili Domain响应相关的类
+ */
+public class PiliDomainModel {
+ private PiliDomainModel() {
+
+ }
+
+ /**
+ * 查询域名列表返回值
+ * 参考文档:域名列表
+ */
+ public class DomainsListResult {
+ /**
+ * Domains 域名列表
+ */
+ public DomainsListItem[] domains;
+ }
+
+ /**
+ * 查询域名列表项
+ * 参考文档:域名列表项
+ */
+ public class DomainsListItem {
+ /**
+ * Type 域名类型
+ */
+ String type;
+ /**
+ * Domain 域名
+ */
+ String domain;
+ /**
+ * CNAME CNAME
+ */
+ String cname;
+ /**
+ * CertEnable 是否配置 SSL 证书
+ */
+ Boolean certEnable;
+ /**
+ * CertName 证书名称
+ */
+ String certName;
+ }
+
+ /**
+ * 查询域名信息返回值
+ * 参考文档:域名信息
+ */
+ public class DomainInfoResult {
+ /**
+ * Domain 域名
+ */
+ public String domain;
+
+ /**
+ * Type 域名类型
+ */
+ public String type;
+
+ /**
+ * Cname CNAME
+ */
+ public String cname;
+
+ /**
+ * ConnectCallback 开播回调配置
+ */
+ public DomainCallbackConfig connectCallback;
+
+ /**
+ * DisconnectCallback 断播回调配置
+ */
+ public DomainCallbackConfig disconnectCallback;
+
+ /**
+ * IPLimit IP 访问限制
+ */
+ public DomainIPLimit iPLimit;
+
+ /**
+ * PlaySecurity 时间戳防盗链配置
+ */
+ public DomainPlaySecurity playSecurity;
+
+ /**
+ * 断流延迟配置
+ * DisconnectDelay 单位:秒,针对直播流短时间内闪断重连的情况,不触发断流回调,避免因为短时间内频繁断流造成大量回调
+ */
+ public long disconnectDelay;
+
+ /**
+ * UrlRewrite URL 改写规则
+ */
+ public DomainUrlRewrite urlRewrite;
+
+ /**
+ * CertEnable 是否配置 SSL 证书
+ */
+ public boolean certEnable;
+
+ /**
+ * CertName 证书名称
+ */
+ public String certName;
+
+ /**
+ * Disable 域名是否为禁用状态
+ */
+ public boolean disable;
+ }
+
+ public class DomainCallbackConfig {
+ /**
+ * Type 回调类型
+ * 可选回调类型为
+ * 留空: 不开启回调功能
+ * GET: 发送GET请求回调,请求参数携带在query中
+ * FORM: 发送POST请求回调,请求参数携带在body中,Content-Type 为
+ * application/x-www-form-urlencoded
+ * JSON: 发送POST请求回调,请求参数携带在body中,Content-Type 为 application/json
+ */
+ public String type;
+
+ /**
+ * URL 回调地址
+ * 支持魔法变量
+ */
+ public String url;
+
+ /**
+ * Timeout 超时时间
+ * 与回调地址的 HTTP 连接超时时间,单位:秒
+ * 默认值为 2 秒,配置范围为 0~10 秒
+ */
+ public long timeout;
+
+ /**
+ * Vars 请求参数
+ * 支持魔法变量,至少需要一组请求参数,规则解析出错误的会设置成空字段
+ */
+ public Map vars;
+
+ /**
+ * RetryTimes 重试次数
+ * 可选范围 0~5 次
+ */
+ public int retryTimes;
+
+ /**
+ * RetryInterval 重试间隔
+ * 可选范围 0~5 秒,单位:秒
+ */
+ public int retryInterval;
+
+ /**
+ * SuccessCode 回调成功的 http code
+ * 为 0 表示通配
+ */
+ public int successCode;
+
+ /**
+ * FailCode 回调失败的 http code
+ * 为 0 表示通配,当 SuccessCode 不为 0 的情况下生效
+ */
+ public int failCode;
+ }
+
+ public class DomainIPLimit {
+ /**
+ * WhiteList 白名单
+ * 允许推拉流的 IP 列表,CIDR 类型
+ * 配置白名单后,黑名单列表将失效
+ */
+ public List whitelist;
+
+ /**
+ * BlackList 黑名单
+ * 限制推拉流的 IP 列表,CIDR 类型
+ */
+ public List blacklist;
+ }
+
+ public class DomainPlaySecurity {
+ /**
+ * Type 防盗链类型
+ * 可选防盗链类型为
+ * - 留空: 默认类型,表示继承直播空间级别配置
+ * - none: 表示关闭鉴权
+ * - tsStartMD5: 有效时间从 TsPart 表示的时间戳开始,到 Range 秒后截止
+ * - tsExpireMD5: 有效时间从现在当前到 TsPart 表示的时间戳为止
+ */
+ public String type;
+
+ /**
+ * Key1 主密钥
+ */
+ public String key1;
+
+ /**
+ * Key2 副密钥
+ * 两个密钥将同时生效,便于线上业务替换密钥
+ */
+ public String key2;
+
+ /**
+ * Range 有效时间
+ * 当 Type 为 "tsStartMD5" 时生效,单位:秒
+ */
+ public int range;
+
+ /**
+ * Rule 签名规则
+ * 支持魔法变量的规则,最终签算结果为所有变量的md5
+ * - $(key): 密钥
+ * - $(path): URL 中的 path 部分
+ * - $(streamKey): URL 中的 hub/stream 部分
+ * - $(streamTitle): URL 中的 stream 部分
+ * - $(path_): URL 中的 path 部分, 表示 path 层级
+ * - $(_): URL 中的 query 字段,举例: key1=val,魔法变量中使用 $(_key1) 表示 val
+ * 举例:
+ * 配置Rule为: $(key)$(path)$(_t)
+ * 魔法变量替换完成后: key/hub/streamTitle1634745600
+ * 对结果进行md5计算,最终签算值为:3bc26fe6b35f5c7efab87778c5b27993
+ */
+ public String rule;
+
+ /**
+ * Rule2 签名规则 2
+ * 两个签名规则将同时生效,便于线上业务更换签名规则
+ */
+ public String rule2;
+
+ /**
+ * TsPart 时间戳字段
+ * URL中表示时间戳的字段名
+ */
+ public String tsPart;
+
+ /**
+ * TsBase 时间戳进制
+ * 可选进制格式为 2-36,即 2 进制到 36 进制,默认使用16进制
+ */
+ public int tsBase;
+
+ /**
+ * SignPart 签名字段
+ * URL中表示token的字段名
+ */
+ public String signPart;
+
+ /**
+ * GapDuration 时间误差值
+ * 针对 tsExpireMD5 生效,避免因签算方与服务器本地时间误差造成的鉴权失败
+ */
+ public int gapDuration;
+ }
+
+ /**
+ * DomainUrlRewrite URL 改写规则配置
+ */
+ public class DomainUrlRewrite {
+ /**
+ * rewriteRules 需要转换处理的 URL 规则配置
+ */
+ public List rewriteRules;
+ }
+
+ /**
+ * DomainUrlRewriteItem URL 改写规则项
+ */
+ public static class DomainUrlRewriteItem {
+ /**
+ * Pattern 匹配规则
+ */
+ public String pattern;
+
+ /**
+ * Replace 改写规则
+ */
+ public String replace;
+ }
+
+ public static final String PUBLISH_RTMP = "publishRtmp";
+ public static final String LIVE_RTMP = "liveRtmp";
+ public static final String LIVE_HLS = "liveHls";
+ public static final String LIVE_HDL = "liveHdl";
+
+ /**
+ * DomainURLRewriteRule URL 改写规则
+ */
+ public static class Rules {
+ /**
+ * Pattern 匹配规则
+ * 针对完整URL的正则表达式,形式如:(.+)/live/(.+)/playlist.m3u8
+ * 括号中的内容允许在 Replace 中使用${n}引用(n表示括号顺序)
+ */
+ public String pattern;
+
+ /**
+ * Replace 改写规则
+ * 希望得到的改写结果,形式如:${1}/hub/${2}.m3u8
+ * 改写后的URL应符合七牛的直播URL规范: :////[]?
+ */
+ public String replace;
+
+ public Rules(String pattern, String replace) {
+ this.pattern = pattern;
+ this.replace = replace;
+ }
+ }
+}
diff --git a/src/main/java/com/qiniu/pili/PiliHubManager.java b/src/main/java/com/qiniu/pili/PiliHubManager.java
new file mode 100644
index 00000000..7a547f7f
--- /dev/null
+++ b/src/main/java/com/qiniu/pili/PiliHubManager.java
@@ -0,0 +1,137 @@
+package com.qiniu.pili;
+
+import com.qiniu.common.Constants;
+import com.qiniu.common.QiniuException;
+import com.qiniu.http.Client;
+import com.qiniu.http.Response;
+import com.qiniu.util.Auth;
+import com.qiniu.util.Json;
+import com.qiniu.util.StringMap;
+
+public class PiliHubManager extends PiliManager {
+ public PiliHubManager(Auth auth) {
+ super(auth);
+ }
+
+ public PiliHubManager(Auth auth, String server) {
+ super(auth, server);
+ }
+
+ public PiliHubManager(Auth auth, String server, Client client) {
+ super(auth, server, client);
+ }
+
+ public PiliHubManager(Auth auth, String server, Client client, String apihost, String apihttpscheme, String appname) {
+ super(auth, server, client, apihost, apihttpscheme, appname);
+ }
+
+ /**
+ * getHubList 查询直播空间列表
+ * 参考文档:直播空间列表
+ * GET /v2/hubs
+ *
+ * @return 获取直播空间列表请求的回复
+ * @throws QiniuException 异常
+ */
+ public PiliHubModel.HubListResult getHubList() throws QiniuException {
+ String url = server + "/v2/hubs";
+ Response response = get(url);
+ return response.jsonToObject(PiliHubModel.HubListResult.class);
+ }
+
+ /**
+ * getHubInfo 查询直播空间信息
+ * 参考文档:查询直播空间信息
+ * GET /v2/hubs/
+ *
+ * @param hub 请求直播空间
+ * @return 获取直播空间列表请求的回复
+ * @throws QiniuException 异常
+ */
+ public PiliHubModel.HubInfoResult getHubInfo(String hub) throws Exception {
+ String url = super.server + "/v2/hubs/" + hub;
+ Response response = get(url);
+ return response.jsonToObject(PiliHubModel.HubInfoResult.class);
+ }
+
+ /**
+ * hubSecurity 修改直播空间推流鉴权配置
+ * 参考文档:修改直播空间推流鉴权配置
+ * POST /v2/hubs//security
+ *
+ * @param hub 请求直播空间名
+ * @param publishSecurity 鉴权方式,可选推流鉴权类型为: expiry: 限时鉴权、expiry_sk: 限时鉴权SK
+ * @param publishKey 密钥
+ * @return 修改直播空间推流鉴权配置 是否错误
+ * @throws QiniuException 异常
+ */
+ public QiniuException hubSecurity(String hub, String publishSecurity, String publishKey) throws QiniuException {
+ StringMap req = new StringMap();
+ req.put("hub", hub);
+ req.put("publishSecurity", publishSecurity);
+ req.put("publishKey", publishKey);
+ byte[] body = Json.encode(req).getBytes(Constants.UTF_8);
+
+ String url = this.server + "/v2/hubs/" + hub + "/security";
+ Response response = post(url, body);
+ return response.isOK() ? null : new QiniuException(response);
+ }
+
+ /**
+ * hubHlsplus 修改直播空间 hls 低延迟配置
+ * 参考文档:修改直播空间
+ * hls 低延迟配置
+ * POST /v2/hubs//hlsplus
+ *
+ * @param hub 请求直播空间名
+ * @param HlsPlus 是否开启 hls 低延迟
+ * @return 修改直播空间 hls 低延迟配置 是否错误
+ * @throws QiniuException 异常
+ */
+ public QiniuException hubHlsplus(PiliHubModel.HubHlsplusRequest param) throws QiniuException {
+ byte[] body = Json.encode(param).getBytes(Constants.UTF_8);
+ String url = this.server + "/v2/hubs/" + param.hub + "/security";
+ Response response = post(url, body);
+ return response.isOK() ? null : new QiniuException(response);
+ }
+
+ /**
+ * hubPersistence 修改直播空间存储配置
+ * 参考文档:修改直播空间存储配置
+ * POST /v2/hubs//persistence
+ *
+ * @param param.hub 请求直播空间名
+ * @param param.storageBucket 存储空间
+ * @param param.liveDataExpireDays 存储过期时间
+ * @return 修改直播空间存储配置是否错误
+ * @throws QiniuException 异常
+ */
+ public QiniuException hubPersistence(PiliHubModel.HubPersistenceRequest param) throws QiniuException {
+ byte[] body = Json.encode(param).getBytes(Constants.UTF_8);
+ String url = this.server + "/v2/hubs/" + param.hub + "/persistence";
+ Response response = post(url, body);
+ return response.isOK() ? null : new QiniuException(response);
+ }
+
+ /**
+ * hubSnapshot 修改直播空间封面配置
+ * 参考文档:修改直播空间存储配置
+ * POST /v2/hubs//snapshot
+ *
+ * @param param.hub 请求直播空间名
+ * @param param.snapshotInterval SnapshotInterval 间隔时间
+ * @return 获取直播空间列表请求的回复
+ * @throws QiniuException 异常
+ */
+ public QiniuException hubSnapshot(PiliHubModel.HubSnapshotRequest param) throws QiniuException {
+ byte[] body = Json.encode(param).getBytes(Constants.UTF_8);
+ String url = this.server + "/v2/hubs/" + param.hub + "/snapshot";
+ Response response = post(url, body);
+ return response.isOK() ? null : new QiniuException(response);
+ }
+}
diff --git a/src/main/java/com/qiniu/pili/PiliHubModel.java b/src/main/java/com/qiniu/pili/PiliHubModel.java
new file mode 100644
index 00000000..ed0e534d
--- /dev/null
+++ b/src/main/java/com/qiniu/pili/PiliHubModel.java
@@ -0,0 +1,224 @@
+package com.qiniu.pili;
+
+import java.util.List;
+
+/**
+ * 这里定义了Pili Hub响应相关的类
+ */
+public class PiliHubModel {
+ /**
+ * GetHubListResponse 查询直播空间列表返回值
+ */
+ public class HubListResult {
+ /**
+ * Items 直播空间列表
+ */
+ GetHubListItem[] items;
+ }
+
+ /**
+ * GetHubListItem 查询直播空间列表项
+ */
+ public class GetHubListItem {
+ /**
+ * Name 直播空间
+ */
+ String name;
+ }
+
+ public class HubInfoResult {
+ /**
+ * Name 直播空间名称
+ */
+ public String name;
+
+ /**
+ * CreatedAt 创建时间,Unix 时间戳
+ */
+ public long createdAt;
+
+ /**
+ * UpdatedAt 更新时间,Unix 时间戳
+ */
+ public long updatedAt;
+
+ /**
+ * Domains 直播空间下的域名列表
+ */
+ public List domains;
+
+ /**
+ * DefaultDomains 直播空间默认域名
+ */
+ public List defaultDomains;
+
+ /**
+ * StorageBucket 存储 bucket
+ */
+ public String storageBucket;
+
+ /**
+ * LiveDataExpireDays 存储过期时间,单位:天
+ */
+ public long liveDataExpireDays;
+
+ /**
+ * PublishSecurity 推流鉴权方式
+ */
+ public String publishSecurity;
+
+ /**
+ * Nrop 鉴黄配置信息
+ */
+ public NropArgs nrop;
+
+ /**
+ * PassiveCodecProfiles 被动转码配置,形式如:720p
+ */
+ public List passiveCodecProfiles;
+
+ /**
+ * Converts 主动转码配置,形式如:720p
+ */
+ public List converts;
+
+ /**
+ * HlsPlus 是否开启 hls 低延迟
+ */
+ public boolean hlsPlus;
+
+ /**
+ * VodDomain 点播域名
+ */
+ public String vodDomain;
+
+ /**
+ * AccessLog 直播日志保存信息
+ */
+ public AccessLogOptions accessLog;
+
+ /**
+ * SnapshotInterval 直播封面的截图间隔,单位:秒
+ */
+ public int snapshotInterval;
+ }
+
+ public class HubDomain {
+
+ /**
+ * Type 域名类型
+ */
+ public String type;
+
+ /**
+ * Domain 域名
+ */
+ public String domain;
+
+ /**
+ * CNAME
+ */
+ public String cname;
+ }
+
+ public class DefaultDomain {
+
+ /**
+ * Type 域名类型
+ */
+ public String type;
+
+ /**
+ * Domain 域名
+ */
+ public String domain;
+ }
+
+ public class NropArgs {
+
+ /**
+ * Enable 是否开启直播空间级别鉴黄功能
+ */
+ public boolean enable;
+
+ /**
+ * Interval 截帧间隔,每个流隔多久进行截帧并鉴黄,单位:秒
+ */
+ public int interval;
+
+ /**
+ * NotifyUrl 回调 URL
+ */
+ public String notifyUrl;
+
+ /**
+ * NotifyRate 通知阈值,鉴黄结果阈值表示 AI 模型判断当前直播画面有多大的概率涉黄,当鉴黄结果阈值大于或等于通知阈值时,将发送回调信息到回调 URL
+ */
+ public double notifyRate;
+ }
+
+ public class AccessLogOptions {
+
+ /**
+ * SaveBucket 存储空间
+ */
+ public String saveBucket;
+
+ /**
+ * ExpireDays 过期天数
+ */
+ public int expireDays;
+ }
+
+ /**
+ * HubHlsplusRequest 修改直播空间 hls 低延迟配置请求参数
+ */
+ public class HubHlsplusRequest {
+ /**
+ * Hub 直播空间
+ */
+ public String hub;
+ /**
+ * HlsPlus 是否开启 hls 低延迟
+ */
+ public Boolean hlsPlus;
+ }
+
+ /**
+ * HubPersistenceRequest 修改直播空间存储配置请求参数
+ */
+ public class HubPersistenceRequest {
+ /**
+ * Hub 直播空间
+ */
+ public String hub;
+
+ /**
+ * StorageBucket 存储空间
+ */
+ public String storageBucket;
+
+ /**
+ * LiveDataExpireDays 存储过期时间
+ * 单位:天
+ */
+ public int liveDataExpireDays;
+ }
+
+ /**
+ * HubSnapshotRequest 修改直播空间封面配置请求参数
+ */
+ public class HubSnapshotRequest {
+ /**
+ * Hub 直播空间
+ */
+ public String hub;
+
+ /**
+ * SnapshotInterval 间隔时间
+ * 单位:秒
+ */
+ public int snapshotInterval;
+ }
+
+}
diff --git a/src/main/java/com/qiniu/pili/PiliManager.java b/src/main/java/com/qiniu/pili/PiliManager.java
new file mode 100644
index 00000000..1bc63313
--- /dev/null
+++ b/src/main/java/com/qiniu/pili/PiliManager.java
@@ -0,0 +1,107 @@
+package com.qiniu.pili;
+
+import com.qiniu.common.QiniuException;
+import com.qiniu.http.Client;
+import com.qiniu.http.MethodType;
+import com.qiniu.http.Response;
+import com.qiniu.util.Auth;
+import com.qiniu.util.StringMap;
+
+public class PiliManager {
+
+ // APIHost 标准 API 服务器地址
+ protected static final String API_HOST = "pili.qiniuapi.com";
+
+ // IAMAPIHost IAM(权限策略) API 服务器地址
+ protected static final String IAMAPIHost = "pili-iam.qiniuapi.com";
+
+ // APIHTTPScheme HTTP 模式
+ protected static final String APIHTTPScheme = "http://";
+
+ // APIHTTPSScheme HTTPS 模式
+ protected static final String APIHTTPSScheme = "https://";
+
+ // DefaultAppName 默认 AppName 名称
+ protected static final String DefaultAppName = "pili";
+
+ protected final Auth auth;
+ protected final String server;
+ protected final Client client;
+ protected final String apihost;
+ protected final String apihttpscheme;
+
+ /*
+ * PiliManager 使用七牛标准的管理鉴权方式
+ *
+ * @param auth - Auth 对象
+ */
+ public PiliManager(Auth auth) {
+ this(auth, API_HOST);
+ }
+
+ public PiliManager(Auth auth, String server) {
+ this(auth, server, new Client());
+ }
+
+ public PiliManager(Auth auth, String server, Client client) {
+ this(auth, server, client, API_HOST, APIHTTPScheme, DefaultAppName);
+ }
+
+ public PiliManager(Auth auth, String server, Client client, String apihost, String apihttpscheme, String appname) {
+ this.auth = auth;
+ this.server = server;
+ this.client = client;
+
+ if (!apihost.isEmpty()) {
+ this.apihost = apihost;
+ } else {
+ this.apihost = API_HOST;
+ }
+
+ if (!apihttpscheme.isEmpty()) {
+ this.apihttpscheme = apihttpscheme;
+ } else {
+ this.apihttpscheme = APIHTTPScheme;
+ }
+
+ if (!appname.isEmpty()) {
+ Client.setAppName(appname);
+ } else {
+ Client.setAppName(DefaultAppName);
+ }
+ }
+
+ /*
+ * 相关请求的方法列表
+ */
+ protected Response get(String url) throws QiniuException {
+ StringMap headers = composeHeader(url, MethodType.GET.toString(), null, Client.FormMime);
+ return client.get(url, headers);
+ }
+
+ protected Response get(String url, byte[] body) throws QiniuException {
+ StringMap headers = composeHeader(url, MethodType.GET.toString(), body, Client.FormMime);
+ return client.get(url, headers);
+ }
+
+ protected Response post(String url, byte[] body) throws QiniuException {
+ StringMap headers = composeHeader(url, MethodType.POST.toString(), body, Client.JsonMime);
+ return client.post(url, body, headers, Client.JsonMime);
+ }
+
+ protected Response put(String url, byte[] body) throws QiniuException {
+ StringMap headers = composeHeader(url, MethodType.PUT.toString(), body, Client.JsonMime);
+ return client.put(url, body, headers, Client.JsonMime);
+ }
+
+ protected Response delete(String url) throws QiniuException {
+ StringMap headers = composeHeader(url, MethodType.DELETE.toString(), null, Client.DefaultMime);
+ return client.delete(url, headers);
+ }
+
+ protected StringMap composeHeader(String url, String method, byte[] body, String contentType) {
+ StringMap headers = auth.authorizationV2(url, method, body, contentType);
+ headers.put("Content-Type", contentType);
+ return headers;
+ }
+}
diff --git a/src/main/java/com/qiniu/pili/PiliStatManager.java b/src/main/java/com/qiniu/pili/PiliStatManager.java
new file mode 100644
index 00000000..a1ef28c9
--- /dev/null
+++ b/src/main/java/com/qiniu/pili/PiliStatManager.java
@@ -0,0 +1,474 @@
+package com.qiniu.pili;
+
+import com.qiniu.common.QiniuException;
+import com.qiniu.http.Client;
+import com.qiniu.http.Response;
+import com.qiniu.util.Auth;
+import com.qiniu.util.StringMap;
+import com.qiniu.util.UrlUtils;
+
+public class PiliStatManager extends PiliManager {
+ public PiliStatManager(Auth auth) {
+ super(auth);
+ }
+
+ public PiliStatManager(Auth auth, String server) {
+ super(auth, server);
+ }
+
+ public PiliStatManager(Auth auth, String server, Client client) {
+ super(auth, server, client);
+ }
+
+ public PiliStatManager(Auth auth, String server, Client client, String apihost, String apihttpscheme,
+ String appname) {
+ super(auth, server, client, apihost, apihttpscheme, appname);
+ }
+
+ /**
+ * FlowDefaultSelect 上下行流量默认查询字段
+ */
+ public static final String FLOW_DEFAULT_SELECT = "flow";
+
+ /**
+ * CodecDefaultSelect 转码使用量默认查询字段
+ */
+ public static final String CODEC_DEFAULT_SELECT = "duration";
+
+ /**
+ * NropDefaultSelect 鉴黄使用量默认查询字段
+ */
+ public static final String NROP_DEFAULT_SELECT = "count";
+
+ /**
+ * getStatUpflow 获取上行流量
+ * 参考文档:获取上行流量
+ * GET
+ * /statd/upflow?$hub=&$domain=&$area=area&begin=&end=&g=&select=flow
+ *
+ * @param param.commonRequest.begin 开始时间,支持格式:20060102、20060102150405
+ * @param param.commonRequest.end 结束时间,支持格式:20060102、20060102150405,超过当前时间,则以当前时间为准,时间范围为左闭右开区间
+ * @param param.commonRequest.g 时间粒度,可取值为 5min、hour、day、month
+ * @param param.select 值字段,用于返回需要查询的数据。可选值为flow,流量,单位:byte。带宽可以从流量转换,公式为
+ * 带宽=流量*8/时间粒度,单位:bps
+ * @param param.where 查询where条件 hub 直播空间 domain 域名 area 区域
+ * 中国大陆(cn)、香港(hk)、台湾(tw)、亚太(apac)、美洲(am)、欧洲/中东/非洲(emea)
+ * @return 获取上行流量请求的回复
+ * @throws QiniuException 异常
+ */
+ public PiliStatModel.StatResponse[] getStatUpflow(PiliStatModel.GetStatUpflowRequest param) throws QiniuException {
+ if (param.select.isEmpty()) {
+ param.select = FLOW_DEFAULT_SELECT;
+ }
+
+ String url = server + "/statd/upflow";
+ StringMap queryMap = new StringMap();
+ queryMap.put("begin", param.commonRequest.begin);
+ queryMap.put("g", param.commonRequest.g);
+ queryMap.put("end", param.commonRequest.end);
+ queryMap.put("select", param.select);
+ queryMap.putNotNull("hub", param.where.get("hub").toString());
+ queryMap.putNotNull("domain", param.where.get("domain").toString());
+ queryMap.putNotNull("area", param.where.get("area").toString());
+ String requestUrl = UrlUtils.composeUrlWithQueries(url, queryMap);
+
+ Response response = get(requestUrl);
+ if (response == null) {
+ throw new QiniuException(response);
+ }
+ return response.jsonToObject(PiliStatModel.StatResponse[].class);
+ }
+
+ /**
+ * groupStatUpflow 分组获取上行流量
+ * 参考文档:分组获取上行流量
+ * GET
+ * /statd/upflow?$hub=&$domain=&$area=area&begin=&end=&g=&group=&select=flow
+ *
+ * @param param.commonRequest.begin 开始时间,支持格式:20060102、20060102150405
+ * @param param.commonRequest.end 结束时间,支持格式:20060102、20060102150405,超过当前时间,则以当前时间为准,时间范围为左闭右开区间
+ * @param param.commonRequest.g 时间粒度,可取值为 5min、hour、day、month
+ * @param param.group 按特定条件将返回数据分组,可取值为条件字段
+ * @param param.select 值字段,用于返回需要查询的数据。可选值为flow,流量,单位:byte。带宽可以从流量转换,公式为
+ * 带宽=流量*8/时间粒度,单位:bps
+ * @param param.where 查询where条件 hub 直播空间 domain 域名 area 区域
+ * 中国大陆(cn)、香港(hk)、台湾(tw)、亚太(apac)、美洲(am)、欧洲/中东/非洲(emea)
+ * @return 获取上行流量请求的回复
+ * @throws QiniuException 异常
+ */
+ public PiliStatModel.StatGroupResponse[] groupStatUpflow(PiliStatModel.GroupStatUpflowRequest param)
+ throws QiniuException {
+
+ if (param.select.isEmpty()) {
+ param.select = FLOW_DEFAULT_SELECT;
+ }
+
+ String url = this.server + "/statd/upflow";
+ StringMap queryMap = new StringMap();
+ queryMap.put("begin", param.commonRequest.begin);
+ queryMap.put("g", param.commonRequest.g);
+ queryMap.put("end", param.commonRequest.end);
+ queryMap.put("select", param.select);
+ queryMap.putNotNull("group", param.group);
+ queryMap.putNotNull("hub", param.where.get("hub").toString());
+ queryMap.putNotNull("domain", param.where.get("domain").toString());
+ queryMap.putNotNull("area", param.where.get("area").toString());
+ String requestUrl = UrlUtils.composeUrlWithQueries(url, queryMap);
+
+ Response response = get(requestUrl);
+ if (response == null) {
+ throw new QiniuException(response);
+ }
+ return response.jsonToObject(PiliStatModel.StatGroupResponse[].class);
+ }
+
+ /**
+ * getStatUpflow 查询直播下行流量
+ * 参考文档:获取直播下行流量
+ * GET
+ * /statd/downflow?$hub=&$domain=&$area=area&begin=&end=&g=&group=&select=flow
+ *
+ * @param param.commonRequest.begin 开始时间,支持格式:20060102、20060102150405
+ * @param param.commonRequest.end 结束时间,支持格式:20060102、20060102150405,超过当前时间,则以当前时间为准,时间范围为左闭右开区间
+ * @param param.commonRequest.g 时间粒度,可取值为 5min、hour、day、month
+ * @param param.select 值字段,用于返回需要查询的数据。可选值为flow,流量,单位:byte。带宽可以从流量转换,公式为
+ * 带宽=流量*8/时间粒度,单位:bps
+ * @param param.where 查询where条件 hub 直播空间 domain 域名 area 区域
+ * 中国大陆(cn)、香港(hk)、台湾(tw)、亚太(apac)、美洲(am)、欧洲/中东/非洲(emea)
+ * @return 获取下行流量请求的回复
+ * @throws QiniuException 异常
+ */
+ public PiliStatModel.StatResponse[] getStatDownflow(PiliStatModel.GetStatDownflowRequest param) throws QiniuException {
+ if (param.select.isEmpty()) {
+ param.select = FLOW_DEFAULT_SELECT;
+ }
+
+ String url = this.server + "/statd/downflow";
+ StringMap queryMap = new StringMap();
+ queryMap.put("begin", param.commonRequest.begin);
+ queryMap.put("g", param.commonRequest.g);
+ queryMap.put("end", param.commonRequest.end);
+ queryMap.put("select", param.select);
+ queryMap.putNotNull("hub", param.where.get("hub").toString());
+ queryMap.putNotNull("domain", param.where.get("domain").toString());
+ queryMap.putNotNull("area", param.where.get("area").toString());
+ String requestUrl = UrlUtils.composeUrlWithQueries(url, queryMap);
+
+ Response response = get(requestUrl);
+ if (response == null) {
+ throw new QiniuException(response);
+ }
+ return response.jsonToObject(PiliStatModel.StatResponse[].class);
+ }
+
+ /**
+ * groupStatDownflow 分组获取下行流量
+ * 参考文档:获取上行流量
+ * GET
+ * /statd/upflow?$hub=&$domain=&$area=area&begin=&end=&g=&select=flow
+ *
+ * @param param.commonRequest.begin 开始时间,支持格式:20060102、20060102150405
+ * @param param.commonRequest.end 结束时间,支持格式:20060102、20060102150405,超过当前时间,则以当前时间为准,时间范围为左闭右开区间
+ * @param param.commonRequest.g 时间粒度,可取值为 5min、hour、day、month
+ * @param param.group 按特定条件将返回数据分组,可取值为条件字段
+ * @param param.select 值字段,用于返回需要查询的数据。可选值为flow,流量,单位:byte。带宽可以从流量转换,公式为
+ * 带宽=流量*8/时间粒度,单位:bps
+ * @param param.where 查询where条件 hub 直播空间 domain 域名 area 区域
+ * 中国大陆(cn)、香港(hk)、台湾(tw)、亚太(apac)、美洲(am)、欧洲/中东/非洲(emea)
+ * @return 获取下行流量请求的回复
+ * @throws QiniuException 异常
+ */
+ public PiliStatModel.StatGroupResponse[] groupStatDownflow(PiliStatModel.GroupStatDownflowRequest param)
+ throws QiniuException {
+ if (param.select.isEmpty()) {
+ param.select = FLOW_DEFAULT_SELECT;
+ }
+
+ String url = server + "/statd/downflow";
+ StringMap queryMap = new StringMap();
+ queryMap.put("begin", param.commonRequest.begin);
+ queryMap.put("g", param.commonRequest.g);
+ queryMap.put("end", param.commonRequest.end);
+ queryMap.put("select", param.select);
+ queryMap.put("group", param.group);
+ queryMap.putNotNull("hub", param.where.get("hub").toString());
+ queryMap.putNotNull("domain", param.where.get("domain").toString());
+ queryMap.putNotNull("area", param.where.get("area").toString());
+ String requestUrl = UrlUtils.composeUrlWithQueries(url, queryMap);
+
+ Response response = get(requestUrl);
+ if (response == null) {
+ throw new QiniuException(response);
+ }
+ return response.jsonToObject(PiliStatModel.StatGroupResponse[].class);
+ }
+
+ /**
+ * getStatCodec 获取直播转码使用量
+ * 参考文档:获取直播转码使用量
+ * GET
+ * /statd/codec?$hub=&$profile=&begin=&end=&g=&group=&select=duration
+ *
+ * @param param.commonRequest.begin 开始时间,支持格式:20060102、20060102150405
+ * @param param.commonRequest.end 结束时间,支持格式:20060102、20060102150405,超过当前时间,则以当前时间为准,时间范围为左闭右开区间
+ * @param param.commonRequest.g 时间粒度,可取值为 5min、hour、day、month
+ * @param param.select 值字段,用于返回需要查询的数据。可选值为flow,流量,单位:byte。带宽可以从流量转换,公式为
+ * 带宽=流量*8/时间粒度,单位:bps
+ * @param param.where 查询where条件 hub 直播空间 domain 域名 area 区域
+ * 中国大陆(cn)、香港(hk)、台湾(tw)、亚太(apac)、美洲(am)、欧洲/中东/非洲(emea)
+ * @return 获取直播转码使用量请求的回复
+ * @throws QiniuException 异常
+ */
+ public PiliStatModel.StatResponse[] getStatCodec(PiliStatModel.GetStatCodecRequest param) throws QiniuException {
+ if (param.select.isEmpty()) {
+ param.select = CODEC_DEFAULT_SELECT;
+ }
+
+ String url = this.server + "/statd/codec";
+ StringMap queryMap = new StringMap();
+ queryMap.put("select", param.select);
+ queryMap.put("begin", param.commonRequest.begin);
+ queryMap.put("g", param.commonRequest.g);
+ queryMap.putWhen("end", param.commonRequest.end, !param.commonRequest.end.isEmpty());
+ queryMap.putWhen("where", param.where, !param.where.isEmpty());
+
+ String requestUrl = UrlUtils.composeUrlWithQueries(url, queryMap);
+ Response response = get(requestUrl);
+ return response.jsonToObject(PiliStatModel.StatResponse[].class);
+ }
+
+ /**
+ * GroupStatCodec 分组获取直播转码使用量
+ * 参考文档:获取直播转码使用量
+ * GET
+ * /statd/codec?$hub=&$profile=&begin=&end=&g=&select=duration
+ *
+ * @param param.commonRequest.begin 开始时间,支持格式:20060102、20060102150405
+ * @param param.commonRequest.end 结束时间,支持格式:20060102、20060102150405,超过当前时间,则以当前时间为准,时间范围为左闭右开区间
+ * @param param.commonRequest.g 时间粒度,可取值为 5min、hour、day、month
+ * @param param.group 按特定条件将返回数据分组,可取值为条件字段
+ * @param param.select 值字段,用于返回需要查询的数据。 duration 时长,单位:毫秒
+ * @param param.where 查询where条件 hub 直播空间 profile 转码规格
+ * @return 获取直播转码使用量请求的回复
+ * @throws QiniuException 异常
+ */
+ public PiliStatModel.StatGroupResponse[] groupStatCodec(PiliStatModel.GroupStatCodecRequest param) throws QiniuException {
+ if (param.select.isEmpty()) {
+ param.select = CODEC_DEFAULT_SELECT;
+ }
+
+ String url = this.server + "/statd/codec";
+ StringMap queryMap = new StringMap();
+ queryMap.put("select", param.select);
+ queryMap.put("begin", param.commonRequest.begin);
+ queryMap.put("group", param.group);
+ queryMap.put("g", param.commonRequest.g);
+ queryMap.putWhen("end", param.commonRequest.end, !param.commonRequest.end.isEmpty());
+ queryMap.putWhen("where", param.where, !param.where.isEmpty());
+
+ String requestUrl = UrlUtils.composeUrlWithQueries(url, queryMap);
+ Response response = get(requestUrl);
+ return response.jsonToObject(PiliStatModel.StatGroupResponse[].class);
+ }
+
+ /**
+ * getStatNrop 获取直播鉴黄使用量
+ * 参考文档:获取鉴黄使用量
+ * GET
+ * /statd/nrop?$assured=&$hub=&begin=&end=&g=&select=count
+ *
+ * @param param.commonRequest.begin 开始时间,支持格式:20060102、20060102150405
+ * @param param.commonRequest.end 时间,支持格式:20060102、20060102150405,超过当前时间,则以当前时间为准,时间范围为左闭右开区间
+ * @param param.commonRequest.g 时间粒度,可取值为 5min、hour、day、month
+ * @param param.select 值字段,用于返回需要查询的数据。可选值为flow,流量,单位:byte。带宽可以从流量转换,公式为
+ * 带宽=流量*8/时间粒度,单位:bps
+ * @param param.where 查询where条件 hub 直播空间 domain 域名 area 区域
+ * 中国大陆(cn)、香港(hk)、台湾(tw)、亚太(apac)、美洲(am)、欧洲/中东/非洲(emea)
+ * @return 获取直播鉴黄使用量请求的回复
+ * @throws QiniuException 异常
+ */
+ public PiliStatModel.StatResponse[] getStatNrop(PiliStatModel.GetStatNropRequest param) throws QiniuException {
+ if (param.select.isEmpty()) {
+ param.select = NROP_DEFAULT_SELECT;
+ }
+
+ String url = this.server + "/statd/nrop";
+ StringMap queryMap = new StringMap();
+ queryMap.put("select", param.select);
+ queryMap.put("begin", param.commonRequest.begin);
+ queryMap.put("g", param.commonRequest.g);
+ queryMap.putWhen("end", param.commonRequest.end, !param.commonRequest.end.isEmpty());
+ queryMap.putWhen("where", param.where, !param.where.isEmpty());
+
+ String requestUrl = UrlUtils.composeUrlWithQueries(url, queryMap);
+ Response response = get(requestUrl);
+ return response.jsonToObject(PiliStatModel.StatResponse[].class);
+ }
+
+ /**
+ * GroupStatNrop 分组获取直播鉴黄使用量
+ * 参考文档:获取直播转码使用量
+ * GET
+ * /statd/nrop?$assured=&$hub=&begin=&end=&g=&group=&select=count
+ *
+ * @param param.commonRequest.begin 开始时间,支持格式:20060102、20060102150405
+ * @param param.commonRequest.end 结束时间,支持格式:20060102、20060102150405,超过当前时间,则以当前时间为准,时间范围为左闭右开区间
+ * @param param.commonRequest.g 时间粒度,可取值为 5min、hour、day、month
+ * @param param.group 按特定条件将返回数据分组,可取值为条件字段
+ * @param param.select 值字段,用于返回需要查询的数据 count 鉴黄次数
+ * @param param.where 查询where条件 hub 直播空间 assured
+ * 鉴黄结果是否确定,true或false
+ * @return 获取直播鉴黄使用量请求的回复
+ * @throws QiniuException 异常
+ */
+ public PiliStatModel.StatGroupResponse[] groupStatNrop(PiliStatModel.GroupStatCodecRequest param) throws QiniuException {
+ if (param.select.isEmpty()) {
+ param.select = CODEC_DEFAULT_SELECT;
+ }
+
+ String url = this.server + "/statd/nrop";
+ StringMap queryMap = new StringMap();
+ queryMap.put("select", param.select);
+ queryMap.put("begin", param.commonRequest.begin);
+ queryMap.put("group", param.group);
+ queryMap.put("g", param.commonRequest.g);
+ queryMap.putWhen("end", param.commonRequest.end, !param.commonRequest.end.isEmpty());
+ queryMap.putWhen("where", param.where, !param.where.isEmpty());
+
+ String requestUrl = UrlUtils.composeUrlWithQueries(url, queryMap);
+ Response response = get(requestUrl);
+ return response.jsonToObject(PiliStatModel.StatGroupResponse[].class);
+ }
+
+ /**
+ * getStatCaster 获取导播台使用量
+ * 参考文档:获取导播台使用量
+ * GET
+ * /statd/caster?$resolution=&$container=&begin=&end=&g=&select=