更新记录
1.0.0(2026-09-30)
新版提交
平台兼容性
uni-app(5.07)
| Vue2 | Vue2插件版本 | Vue3 | Vue3插件版本 | Chrome | Safari | app-vue | app-vue插件版本 | app-nvue | app-nvue插件版本 | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| √ | 1.0.0 | √ | 1.0.0 | × | × | √ | 1.0.0 | √ | 1.0.0 | - | - | - |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | - | - | - | - | - | - |
uni-app x(5.07)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| × | × | 7.0 | 12 | × | × |
csj-short-drama
穿山甲短剧(内容 SDK) 的 UTS 插件,为 uni-app x App 端提供短剧内容接入与广告解锁能力。
插件只提供 API,不提供列表 UI 组件;业务侧自行实现首页、频道、搜索、收藏、观看记录等页面,并通过插件的
open() / openDraw() / openHome() 打开由穿山甲原生渲染的播放页 / 滑滑流 / 聚合页。
支持能力
| 能力 | Android | iOS | 说明 |
|---|---|---|---|
| 初始化 / 启动(含广告 SDK 自动初始化) | ✅ | ✅ | 插件内部完成广告 SDK → 内容 SDK 的初始化时序 |
| 短剧列表 / 推荐 / 分类 / 搜索 / 详情 | ✅ | ✅ | 对应官方 IDJXService / DJXPlayletManager |
| 观看记录 / 收藏列表 / 清空观看记录 | ✅ | ✅ | — |
| 点赞、取消点赞、收藏、取消收藏 | ✅ | ❌ | iOS 原生未公开该接口,调用返回 -3,需业务侧自行维护状态 |
| 分集解锁状态查询 | ✅ | ⚠️ | iOS 无独立接口,返回空列表;解锁信息随 getInfo() 的 rawData 下发 |
播放页 open() |
✅ | ✅ | 原生全屏播放页 |
滑滑流 openDraw() |
✅ | ✅ | 沉浸式竖滑流 |
聚合页 openHome() |
✅ | ✅ | 原生短剧聚合页 |
SDK 广告解锁(unlockAdMode: 'sdk') |
✅ | ✅ | 广告位来自 SDK_Setting.json |
自定义广告解锁(unlockAdMode: 'custom') |
✅ | ✅ | 业务侧自己播激励视频,回传 completeUnlock() |
| 解锁记录绑定业务账号 | ✅ | ✅ | bindUnlockRecord() / unbindUnlockRecord() |
平台与版本
| 项 | 值 |
|---|---|
| 支持框架 | uni-app x(App 端);不支持浏览器、小程序、鸿蒙 |
| Android | minSdkVersion 24,官方 SDK com.pangle.cn:pangrowth-djx-sdk-lite:3.0.0.2 |
| iOS | deploymentTarget 12.0,CocoaPods PangrowthX/shortplay 2.9.0.6 + TTSDKFramework/{LivePull,Player-SR} 1.46.2.7-premium |
| HBuilderX | ^5.25(建议 5.26 及以上) |
| 调试方式 | 必须自定义基座或云打包 App;标准基座不可用 |
接入前必读
- 仅 App 真机环境:插件是原生扩展,浏览器、小程序、标准基座都无法运行。改完插件代码后必须重新制作自定义基座。
- 必须先开通穿山甲短剧内容能力:在穿山甲后台「内容输出 → 接入管理」录入应用并下载
SDK_Setting.json,否则初始化会报04016。 - 必须勾选穿山甲广告模块:
manifest.json → App 模块配置 → uni-ad(穿山甲 / GroMore)。 插件通过官方广告 SDK 的类(AndroidTTAdSdk、iOSBUAdSDKManager)初始化广告 SDK,这些类由 uni-ad 的穿山甲模块提供; 未勾选时插件会抛出adInitFailed(Android-7004:TTAdSdk class not found)。 - 短剧只支持广告解锁:不支持 IAP、会员付费,也不允许把全部剧集设为免费。
免费集数上限为
min(20 集, 全剧前 20%),一次激励视频最多解锁 10 集。违反规则会被平台强制回退到兜底广告,严重会被封禁。 - 建议激励视频瀑布流中包含穿山甲广告且填充率 > 6%,详见官方规则。
- 双端
SDK_Setting.json是两份不同的文件:Android 录包名、iOS 录 Bundle ID,不能互相复制。 - Android 运行时不要勾选 HBuilderX 的「清理构建缓存」,否则可能出现运行时报错。
- iOS 的插件资源要与构建产物版本一致:
utssdk/app-ios/Resources/下的CSJAdSDK.bundle等资源带版本号, 与本次打包实际链接的广告 SDK 版本不一致会导致error_code=2。重新打包后请运行scripts/verify-ios-resources.sh校验(见 iOS 资源版本一致性)。 - 隐私合规:广告 SDK 初始化会读取设备信息,请在用户同意隐私政策之后再调用
createShortDrama()。
目录结构
uni_modules/csj-short-drama/
├── package.json
├── readme.md
├── scripts/
│ └── verify-ios-resources.sh # iOS 资源版本一致性校验(见文末)
└── utssdk/
├── interface.uts # 公共类型与接口定义(API 契约)
├── app-android/
│ ├── config.json # Maven 依赖:pangrowth-base / pangrowth-djx-sdk-lite
│ ├── AndroidManifest.xml # 权限声明 + 组件宿主 Activity
│ ├── DJXNativeBridge.kt # 官方 SDK 调用桥接层
│ ├── DJXHostActivity.kt # 聚合页 / 滑滑流 / 播放页 的 Fragment 容器
│ ├── res/values/styles.xml
│ ├── index.uts # UTS 层实现
│ └── assets/SDK_Setting.json # ⚠️ 必须替换为当前应用的后台配置
└── app-ios/
├── config.json # CocoaPods 依赖 + 系统 framework 链接
├── DramaModuleBridge.swift # 原生桥接层(含广告 SDK 反射初始化)
├── index.uts # UTS 层实现
└── Resources/ # ⚠️ 整个目录会被平铺拷贝到 .app 根目录
├── SDK_Setting.json # ⚠️ 必须替换为当前应用的后台配置
├── *.metallib # 播放器 Metal 着色器(缺失会导致一进播放器就闪退)
├── bmf_hydra/ # BMF 效果框架着色器
├── DJXSDK.bundle # 短剧内容 UI 资源(封面 / 图标 / 文案)
├── CSJAdSDK.bundle # 穿山甲广告 UI 资源(含 version.txt,版本必须与构建产物一致)
├── VeLive.bundle # 直播 PrivacyInfo
└── APMInsight*.bundle / Rangers*.bundle # APM / 埋点资源
配置文件
Android
把当前 Android 应用对应的 SDK_Setting.json 放到:
uni_modules/csj-short-drama/utssdk/app-android/assets/SDK_Setting.json
检查三项:
init.site_id是当前 Android 应用的穿山甲媒体 ID;license_config[].PackageName与工程 Android 包名一致;- 后台已为该应用开通短剧功能并配置短剧广告位。
iOS
把当前 iOS 应用对应的 SDK_Setting.json 放到:
uni_modules/csj-short-drama/utssdk/app-ios/Resources/SDK_Setting.json
检查三项:
init.site_id是当前 iOS 应用的穿山甲媒体 ID;license_config[].BundleId与工程 Bundle ID 一致;- 后台已为该应用开通短剧功能并配置短剧广告位。
Resources/目录里除SDK_Setting.json之外的文件都不要手改,它们必须与本次打包实际使用的 SDK 版本一致,详见 iOS 资源版本一致性。
adSiteId 与 adpid
| 名称 | 含义 | 是否需要传给插件 |
|---|---|---|
adSiteId |
穿山甲广告 SDK 媒体 ID(= SDK_Setting.json 的 init.site_id) |
可选。不传时插件读 SDK_Setting.json;当前仅 iOS 生效,Android 始终读 assets/SDK_Setting.json |
adpid |
激励视频广告位 ID | 只有 unlockAdMode: 'custom' 时业务侧自己使用,不需要传给 createShortDrama() |
同一个
SDK_Setting.json里两者都有:init.site_id是媒体 ID(广告 SDK 用),短剧解锁广告位由后台配置下发。
快速开始
1. 导入并创建实例
import { createShortDrama } from '@/uni_modules/csj-short-drama'
import type { ShortDrama, ShortDramaError, ShortDramaListResult } from '@/uni_modules/csj-short-drama'
let shortDrama: ShortDrama | null = null
function getShortDrama(): ShortDrama {
const current = shortDrama
if (current != null) return current
const sdk = createShortDrama({ debug: true })
sdk.onError((err: ShortDramaError) => {
console.error('短剧插件错误', err.code, err.message)
})
shortDrama = sdk
return sdk
}
关于初始化时序:官方要求「广告 SDK 初始化成功后才能初始化内容 SDK」。本插件已把这一时序封装在内部:
createShortDrama()
↓ 1) 广告 SDK(Android TTAdSdk / iOS BUAdSDKManager,appId = init.site_id)
↓ 成功(失败则 15s / 10s 看门狗兜底,交给官方 SDK 报出明确错误码)
↓ 2) 内容 SDK DJXSdk / DJXManager init + start
↓ 3) 触发 onLoad;此前发起的 open* / get* 调用会被排队或提示稍后重试
- 不需要业务侧先调用
uni.createRewardedVideoAd()之类的广告 API 来「预热」广告 SDK。 - 推荐在 App 启动后(例如首页
onLoad)就先createShortDrama(),把 SDK 启动的几秒钟提前消化掉。 - 启动中的调用行为差异:iOS 会自动排队,启动成功后补跑本次调用;Android 会立即回调
-1(消息里提示"正在等待广告SDK初始化,通常几秒内完成,请稍后重试")并自动重排启动,业务侧稍后重试即可。
2. 拉列表并打开播放页
function loadRecommend() {
getShortDrama().getRecommendedList({ page: 1, pageSize: 20 }, (res: ShortDramaListResult) => {
// res.list: Array<ShortDramaInfo>
console.log('推荐列表', res.list.length)
}, (err: ShortDramaError) => {
console.error('拉取失败', err.code, err.message)
})
}
function playOne(dramaId: number, episode: number) {
getShortDrama().open({
dramaId: dramaId,
startEpisode: episode,
freeEpisodeCount: 1,
unlockEpisodeCount: 1,
unlockAdMode: 'sdk'
}, () => {
console.log('播放页已打开')
}, (err: ShortDramaError) => {
console.error('打开失败', err.code, err.message)
})
}
常用能力示例
列表 / 分类 / 搜索
const sdk = getShortDrama()
// 全量列表
sdk.getList({ page: 1, pageSize: 20, order: 'default' }, onList, onFail)
// 推荐列表
sdk.getRecommendedList({ page: 1, pageSize: 20 }, onList, onFail)
// 分类列表(分类名由 getCategories 下发)
sdk.getCategories((res: ShortDramaCategoryListResult) => {
console.log('分类', res.list.map((item) => item.name))
}, onFail)
// 按分类取剧
sdk.getListByCategory({ category: '霸总', page: 1, pageSize: 20 }, onList, onFail)
// 搜索
sdk.search({ keyword: '总裁', fuzzy: true, page: 1, pageSize: 20 }, onList, onFail)
// 按 ID 查详情(dramaId 与 dramaIds 二选一)
sdk.getInfo({ dramaId: 12345 }, onList, onFail)
sdk.getInfo({ dramaIds: [12345, 67890] }, onList, onFail)
观看记录 / 收藏 / 清空记录
sdk.getWatchHistory({ page: 1, pageSize: 20 }, onList, onFail)
sdk.getFavorites({ page: 1, pageSize: 20 }, onList, onFail)
sdk.clearWatchHistory(() => { console.log('已清空观看记录') }, onFail)
点赞与收藏(仅 Android 生效)
sdk.like({ dramaId: 12345, episode: 1 }, () => {}, onFail)
sdk.unlike({ dramaId: 12345, episode: 1 }, () => {}, onFail)
sdk.favorite({ dramaId: 12345, episode: 1 }, () => {}, onFail)
sdk.unfavorite({ dramaId: 12345, episode: 1 }, () => {}, onFail)
iOS 原生头文件未公开点赞 / 收藏接口,这四个方法在 iOS 上会回调
-3,请业务侧自行维护状态。
分集解锁状态(Android)
sdk.getEpisodeUnlockStatus({ dramaId: 12345, freeEpisodeCount: 1 }, (res: ShortDramaEpisodeUnlockStatusResult) => {
const locked = res.list.filter((item) => item.locked).map((item) => item.episodeIndex)
console.log('锁定集', locked)
}, onFail)
iOS 无独立接口,返回空列表;解锁信息随
getInfo()返回的rawData一并下发。
打开播放页 open()
sdk.open({
dramaId: 12345,
startEpisode: 1, // 起播集数,默认 1
freeEpisodeCount: 1, // 前置免费集数,默认 1(上限 20 且不超过全剧前 20%)
unlockEpisodeCount: 1, // 一次激励视频解锁集数,默认 1(上限 10)
unlockAdMode: 'sdk', // 'sdk' 用 SDK_Setting.json 广告;'custom' 业务侧自己播广告
hideTopInfo: false, // 隐藏顶部信息
hideBottomInfo: false, // 隐藏底部信息
hideLikeButton: false, // 隐藏点赞按钮
hideFavorButton: false, // 隐藏收藏按钮
hideMore: false, // 隐藏更多按钮
hideBack: false, // 隐藏返回按钮
hideRewardDialog: false, // 隐藏激励广告确认弹窗
topOffset: 0, // 顶部偏移 px(Android)
bottomOffset: -1 // 底部偏移 px(Android)
}, onOpen, onFail)
打开滑滑流 openDraw()
sdk.openDraw({
channelType: 'recommendTheater', // 'recommend' | 'theater' | 'recommendTheater'
hideChannelName: false,
hideDramaInfo: false, // Android
hideDramaEnter: false,
enableRefresh: true, // 仅 Android 生效
hideClose: false,
dramaFree: 1, // 滑滑流免费集数
topDramaId: 0, // 置顶短剧,0 表示不指定
freeEpisodeCount: 1, // iOS 忽略该项,使用 dramaFree
unlockEpisodeCount: 1,
unlockAdMode: 'sdk'
}, onOpen, onFail)
打开原生聚合页 openHome()
sdk.openHome({
showChangeBtn: true, // 换一换按钮(当前 Android 生效)
showPageTitle: true,
showBackBtn: true,
topOffset: 0, // 顶部偏移 px(当前 Android 生效)
topDramaId: 0,
freeEpisodeCount: 1,
unlockEpisodeCount: 1,
unlockAdMode: 'sdk'
}, onOpen, onFail)
自定义广告解锁(unlockAdMode: 'custom')
默认的 'sdk' 模式不需要监听解锁事件,也不需要回传任何东西。只有 'custom' 模式才需要业务侧自己播激励视频,
并把结果回传,否则解锁任务会一直挂起。
解锁事件分三段:unlockStart(开始)→ unlockRequired(需要业务侧展示广告)→ unlockEnd(结束)。
import type { ShortDramaUnlockEvent, ShortDramaError } from '@/uni_modules/csj-short-drama'
let rewardAd: RewardedVideoAd | null = null
let pendingTaskId = ''
let rewardShownTaskId = ''
function completePending(success: boolean, extra: UTSJSONObject) {
if (pendingTaskId.length == 0) return
getShortDrama().completeUnlock({ taskId: pendingTaskId, success: success, extra: extra })
pendingTaskId = ''
rewardShownTaskId = ''
}
function ensureRewardAd(): RewardedVideoAd {
const current = rewardAd
if (current != null) return current
const created = uni.createRewardedVideoAd({ adpid: '你的激励视频广告位 ID' })
rewardAd = created
// 广告实例的生命周期回调只注册一次,不要放进 onUnlockEvent 里
created.onLoad((_res: any) => {
if (pendingTaskId.length == 0 || rewardShownTaskId == pendingTaskId) return
rewardShownTaskId = pendingTaskId
// 广告真实展示「之前」通知短剧 SDK
getShortDrama().notifyUnlockAdWillShow({ taskId: pendingTaskId })
created.show()
})
created.onClose((res: RewardedVideoAdClose) => {
completePending(res.isEnded == true, { isEnded: res.isEnded } as UTSJSONObject)
})
created.onError((err: any) => {
// 加载/展示失败也要结束任务,否则解锁流程会卡住
completePending(false, { error: err } as UTSJSONObject)
})
return created
}
getShortDrama().onUnlockEvent((event: ShortDramaUnlockEvent) => {
if (event.type != 'unlockRequired') return
pendingTaskId = event.taskId
rewardShownTaskId = ''
ensureRewardAd().load()
})
要点:
taskId必须来自当前unlockRequired事件;同一次任务不要重复回传。- 用户跳过广告、广告加载失败、展示失败,都要用
completeUnlock({ taskId, success: false })结束任务。 - 广告真实曝光时回传 CPM(
notifyUnlockAdWillShow({ taskId, cpm })与completeUnlock({ taskId, success, cpm })); 拿不到时传空字符串''。CPM 长期为空会被短剧 SDK 强制切换成 SDK 直出广告,并屏蔽自定义解锁逻辑。 - 自定义瀑布流必须包含穿山甲广告并产生真实曝光。
解锁记录绑定业务账号
有账号体系时,登录成功后由业务服务端用穿山甲后台的 server key 生成签名串
(格式 nonce=...&ouid=...×tamp=...&sign=...),客户端只传这个串。不要把 server key 下发或保存在客户端。
// params 由业务服务端返回,ouid 为稳定的业务账号 ID
getShortDrama().bindUnlockRecord({ params: serverSignedParams }, (res) => {
console.log('绑定成功', res.bound, res.extra)
}, (err: ShortDramaError) => {
console.error('绑定失败', err.code, err.message)
})
// 账号切换:先等旧账号解绑成功,再绑定新账号
getShortDrama().unbindUnlockRecord(() => {
// 再调用 bindUnlockRecord({ params: newAccountParams })
}, onFail)
// 查询当前是否已绑定
const bound: boolean = getShortDrama().isUnlockRecordBound()
建议在读取解锁状态或打开播放页之前完成绑定;未绑定时按游客处理,解锁记录可能与设备关联。
只在业务账号真实退出登录时调用 unbindUnlockRecord(),关闭播放页或 destroy() 不应解绑。
API 参考
createShortDrama(options?)
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
adSiteId |
string |
'' |
穿山甲广告 SDK 媒体 ID;不传时读取 SDK_Setting.json 的 init.site_id。当前仅 iOS 生效 |
debug |
boolean |
false |
打开 SDK 调试日志 |
teenagerMode |
boolean |
false |
青少年模式(开启后不返回短剧内容) |
ShortDrama 方法
| 方法 | 说明 |
|---|---|
getList(options, success?, fail?) |
全量短剧列表 |
getRecommendedList(options, success?, fail?) |
推荐短剧列表 |
getInfo(options, success?, fail?) |
按 dramaId / dramaIds 查询短剧信息 |
getCategories(success?, fail?) |
分类列表 |
getListByCategory(options, success?, fail?) |
按分类取剧 |
search(options, success?, fail?) |
搜索短剧 |
getWatchHistory(options, success?, fail?) |
观看记录 |
getFavorites(options, success?, fail?) |
收藏列表 |
clearWatchHistory(success?, fail?) |
清空观看记录 |
getEpisodeUnlockStatus(options, success?, fail?) |
分集解锁 / 锁定状态(iOS 返回空列表) |
like / unlike(options, success?, fail?) |
点赞 / 取消点赞(iOS 返回 -3) |
favorite / unfavorite(options, success?, fail?) |
收藏 / 取消收藏(iOS 返回 -3) |
open(options, success?, fail?) |
打开原生播放页 |
openDraw(options, success?, fail?) |
打开原生滑滑流 |
openHome(options, success?, fail?) |
打开原生聚合页 |
bindUnlockRecord(options, success?, fail?) |
绑定业务账号的解锁记录 |
unbindUnlockRecord(success?, fail?) |
解绑业务账号 |
isUnlockRecordBound() |
是否已绑定 |
notifyUnlockAdWillShow(options) |
通知「业务侧激励视频即将展示」(自定义解锁) |
completeUnlock(options) |
回传业务侧解锁结果(自定义解锁) |
destroy() |
释放实例,清空全部事件监听与排队调用 |
onLoad / offLoad |
SDK 启动完成事件 |
onError / offError |
错误事件 |
onPlayEvent / offPlayEvent |
播放页事件 |
onDrawEvent / offDrawEvent |
滑滑流事件 |
onHomeEvent / offHomeEvent |
聚合页事件 |
onAdEvent / offAdEvent |
广告事件 |
onUnlockEvent / offUnlockEvent |
解锁事件 |
offXxx(callback?)是清空该通道的全部监听(原生闭包在不同平台无法做相等比较),不是精确移除单个回调。
Options
ShortDramaListOptions
| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
page |
number |
1 |
页码,从 1 开始 |
pageSize |
number |
20 |
每页数量 |
order |
'default' \| 'reverse' |
'default' |
排序 |
ShortDramaInfoOptions
| 字段 | 类型 | 说明 |
|---|---|---|
dramaId |
number \| string |
单部短剧 ID,与 dramaIds 二选一 |
dramaIds |
Array<any> |
短剧 ID 列表,与 dramaId 二选一 |
ShortDramaSearchOptions
| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
keyword |
string |
— | 必填,搜索关键词 |
fuzzy |
boolean |
true |
是否模糊搜索 |
page / pageSize |
number |
1 / 20 |
分页 |
ShortDramaCategoryListOptions
| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
category |
string |
— | 必填,分类名称,如「霸总」 |
page / pageSize |
number |
1 / 20 |
分页 |
order |
'default' \| 'reverse' |
'default' |
排序 |
ShortDramaEpisodeUnlockStatusOptions
| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
dramaId |
number \| string |
— | 必填 |
freeEpisodeCount |
number |
0 |
前置免费观看集数 |
ShortDramaUserActionOptions
| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
dramaId |
number \| string |
— | 必填 |
episode |
number |
1 |
集数 |
ShortDramaOpenOptions
| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
dramaId |
number \| string |
— | 必填 |
startEpisode |
number |
1 |
起播集数 |
freeEpisodeCount |
number |
1 |
前置免费集数(上限 20 且不超过全剧前 20%) |
unlockEpisodeCount |
number |
1 |
一次激励视频解锁集数(上限 10,不支持一次解锁全集) |
unlockAdMode |
'sdk' \| 'custom' |
'sdk' |
解锁广告来源 |
hideBack |
boolean |
false |
隐藏返回按钮 |
hideTopInfo |
boolean |
false |
隐藏顶部信息 |
hideBottomInfo |
boolean |
false |
隐藏底部短剧信息 |
hideMore |
boolean |
false |
隐藏更多按钮 |
hideLikeButton |
boolean |
false |
隐藏点赞按钮 |
hideFavorButton |
boolean |
false |
隐藏收藏按钮 |
hideRewardDialog |
boolean |
false |
隐藏激励广告确认弹窗 |
topOffset |
number |
0 |
顶部偏移 px(Android) |
bottomOffset |
number |
-1 |
底部偏移 px(Android) |
ShortDramaDrawOptions
| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
channelType |
'recommend' \| 'theater' \| 'recommendTheater' |
'recommend' |
频道类型 |
hideChannelName |
boolean |
false |
隐藏频道名 / 顶部 tab |
hideDramaInfo |
boolean |
false |
隐藏底部短剧信息(Android) |
hideDramaEnter |
boolean |
false |
隐藏底部短剧入口 |
enableRefresh |
boolean |
true |
下拉刷新(仅 Android 生效) |
hideClose |
boolean |
false |
隐藏关闭按钮 |
dramaFree |
number |
1 |
滑滑流免费集数(iOS 用这个值,freeEpisodeCount 被忽略) |
topDramaId |
number \| string |
0 |
置顶短剧,0 表示不指定 |
freeEpisodeCount |
number |
1 |
进入播放页后的免费集数(Android) |
unlockEpisodeCount |
number |
1 |
每次激励视频解锁集数 |
unlockAdMode |
'sdk' \| 'custom' |
'sdk' |
解锁广告来源 |
ShortDramaHomeOptions
| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
showChangeBtn |
boolean |
true |
显示换一换按钮(Android) |
showPageTitle |
boolean |
true |
显示页面标题 |
showBackBtn |
boolean |
true |
显示返回按钮 |
topOffset |
number |
0 |
顶部偏移 px(Android) |
topDramaId |
number \| string |
0 |
置顶短剧 |
freeEpisodeCount |
number |
1 |
进入播放页后的免费集数 |
unlockEpisodeCount |
number |
1 |
每次激励视频解锁集数 |
unlockAdMode |
'sdk' \| 'custom' |
'sdk' |
解锁广告来源 |
NotifyUnlockAdWillShowOptions / CompleteUnlockOptions
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
taskId |
string |
✅ | unlockRequired 事件返回的任务 ID |
cpm |
string |
否 | 广告 CPM,不传为 '';建议在真实曝光时回传 |
success(仅 completeUnlock) |
boolean |
✅ | 激励视频是否完整观看并允许本次解锁 |
extra(仅 completeUnlock) |
UTSJSONObject |
否 | 透传给短剧 SDK 的附加信息 |
ShortDramaUnlockRecordBindOptions
| 字段 | 类型 | 说明 |
|---|---|---|
params |
string |
服务端签名的绑定串,格式 nonce=...&ouid=...×tamp=...&sign=... |
返回值与数据类型
ShortDramaListResult
| 字段 | 类型 | 说明 |
|---|---|---|
list |
Array<ShortDramaInfo> |
短剧列表 |
extra |
UTSJSONObject |
原生 SDK 附加数据 |
ShortDramaInfo
| 字段 | 类型 | 说明 |
|---|---|---|
dramaId |
number |
短剧 ID |
title |
string |
标题 |
coverUrl |
string |
封面图 |
summary |
string |
简介 |
categoryId / categoryName |
number / string |
分类 |
currentEpisode |
number |
当前集 |
totalEpisodes |
number |
总集数 |
groupId |
number |
短剧分组 / 合集 ID |
unlockedEpisodeIndex |
number |
已解锁到的集 |
styleType |
number |
样式类型 |
durationSeconds |
number |
时长(秒) |
rawData |
UTSJSONObject |
原生原始数据,官方 SDK 新增字段、iOS 解锁状态等从这里读 |
部分字段(
groupId/styleType/durationSeconds等)在个别原生版本上没有返回值,会取默认值0; 新接入时建议先用rawData打印一次真实结构再决定用哪些字段。
ShortDramaCategoryListResult / ShortDramaCategoryInfo
| 字段 | 类型 | 说明 |
|---|---|---|
list |
Array<ShortDramaCategoryInfo> |
{ name: string, rawData: UTSJSONObject } |
extra |
UTSJSONObject |
附加数据 |
ShortDramaEpisodeUnlockStatusResult / …Info
| 字段 | 类型 | 说明 |
|---|---|---|
list |
Array<{ episodeIndex: number, locked: boolean, rawData: UTSJSONObject }> |
分集状态 |
extra |
UTSJSONObject |
附加数据 |
ShortDramaUnlockRecordBindResult
| 字段 | 类型 | 说明 |
|---|---|---|
bound |
boolean |
是否已完成绑定 |
extra |
UTSJSONObject |
原生返回的附加信息(如 loginType) |
ShortDramaError
| 字段 | 类型 | 说明 |
|---|---|---|
code |
number |
错误码 |
message |
string |
错误信息 |
detail |
UTSJSONObject |
原生 SDK 附加信息(可能为空) |
事件说明
| 事件 | 常见 type |
说明 |
|---|---|---|
onLoad |
— | SDK 启动完成,payload 形如 { stage: 'start', ... };注册时若已启动会立即补发一次 |
onError |
— | 初始化失败(stage: 'error')或广告 SDK 失败(type: adInitFailed / adStartFailed) |
onPlayEvent |
requestStart、requestSuccess、requestFail、play、pause、resume、completion、over、progress、close |
播放页事件 |
onDrawEvent |
同上 + load |
滑滑流事件 |
onHomeEvent |
requestStart、requestSuccess、prepareFail、createFail、load、open、close |
聚合页事件 |
onAdEvent |
request、requestFail、show、clicked、rewardVerify |
广告事件 |
onUnlockEvent |
unlockStart、unlockRequired、unlockEnd |
解锁事件,详见上文 |
事件对象通用字段:
| 字段 | 类型 | 说明 |
|---|---|---|
type |
string |
事件类型 |
data |
UTSJSONObject |
附加数据 |
drama |
ShortDramaInfo |
当前短剧(onPlayEvent 提供,部分原生化生命周期事件可能为空) |
code |
number |
错误码,成功为 0 |
message |
string |
错误 / 状态信息 |
getShortDrama().onLoad((res: UTSJSONObject) => { console.log('SDK 已启动', res) })
getShortDrama().onError((err: ShortDramaError) => { console.error('短剧错误', err.code, err.message) })
getShortDrama().onPlayEvent((event: ShortDramaPlayEvent) => { console.log('play', event.type, event.code) })
平台差异
| 项 | Android | iOS |
|---|---|---|
adSiteId 参数 |
忽略,固定读 assets/SDK_Setting.json 的 init.site_id |
生效,不传则读 Resources/SDK_Setting.json |
| 点赞 / 收藏 / 取消 | 生效 | 回调 -3(原生未公开),业务侧自行维护 |
getEpisodeUnlockStatus() |
真实返回分集状态 | 返回空列表,改从 getInfo().rawData 读取 |
openDraw().enableRefresh |
生效 | 忽略(原生未暴露该字段) |
openDraw().freeEpisodeCount |
生效 | 忽略,iOS 使用 dramaFree |
openHome().showChangeBtn / topOffset |
生效 | 以 SDK 实际暴露的属性为准(插件会尝试写入,未暴露则忽略) |
| 启动中的 API 调用 | 立即回调 -1 并自动重排启动,稍后重试 |
自动排队,启动成功后补跑 |
| 原生页面宿主 | DJXHostActivity(Fragment 容器) |
直接 present 原生 UIViewController |
offXxx(callback?) 两个平台都已统一为「清空该通道全部监听」。
接入时需要改动的文件(改动说明)
必须改动
| 文件 / 位置 | 改什么 |
|---|---|
utssdk/app-android/assets/SDK_Setting.json |
整体替换为当前 Android 应用在穿山甲后台下载的配置 |
utssdk/app-ios/Resources/SDK_Setting.json |
整体替换为当前 iOS 应用在穿山甲后台下载的配置(与 Android 那份不同) |
manifest.json → *.distribute.modules.uni-ad |
必须按平台分开配置(写法见「注意事项」第 9 条):Android 需含 gm,否则插件反射 TTAdSdk 报 -7004;iOS 不能含 gm,否则 archive 报 Multiple commands produce .../TTSDK*.framework |
manifest.json → app-android.distribute.packageNamemanifest.json → app-ios.distribute.*(Bundle ID) |
与穿山甲后台录入的包名 / Bundle ID 完全一致,否则报 04016 |
| 业务页面 | 自行实现短剧列表 UI、入口、搜索页、收藏页等(插件不含 UI) |
不要改动
| 文件 | 原因 |
|---|---|
utssdk/app-ios/Resources/ 下除 SDK_Setting.json 外的文件 |
与本次打包链接的 SDK 版本一一对应;CSJAdSDK.bundle 版本不一致会让广告 SDK 直接初始化失败(error_code=2) |
utssdk/app-ios/config.json 的 pod 版本 |
LivePull / Player-SR / shortplay 三者版本是互相咬合的,随手换版本会引发重复动态库(Multiple commands produce)或符号缺失 |
utssdk/app-ios/Resources/*.metallib |
二进制着色器,与播放器 pod 版本绑定;缺失或换版本会导致一进播放器就闪退 |
utssdk/app-android/config.json 的 Maven 依赖 |
与官方 SDK 版本一致,改前请确认官方新版本号 |
需要重新打包的场景
改动 utssdk/ 下任何原生代码或资源(.kt / .swift / .json / .bundle / .metallib)后,
必须重新「制作自定义基座」或重新云打包;只改 .uvue 业务代码不需要。
注意事项
- 合规是硬约束,不是建议:短剧必须走广告解锁,且必须有真实广告曝光。免费集数 / 解锁集数由短剧 SDK 强校验, 规避会导致平台强制走兜底广告,严重会被封禁(官方规则)。
- 不要缓存短剧内容渲染:
04005 短剧不存在表示内容已下线,应实时拉取列表后再渲染。 - 失败必须收尾:自定义解锁在用户跳过广告、广告加载失败、展示失败时都要
completeUnlock({ success: false }), 否则当前剧集一直处于解锁中,用户无法继续。 - CPM 不能长期为空:空 CPM 会被 SDK 判定为异常,强制切回 SDK 直出广告并屏蔽自定义解锁。
destroy()的时机:页面 / 模块不再使用短剧实例时调用,清空事件监听;但不要在此时调unbindUnlockRecord()。- 权限(Android):插件在
AndroidManifest.xml声明了READ_PHONE_STATE、ACCESS_NETWORK_STATE、ACCESS_WIFI_STATE、READ_EXTERNAL_STORAGE、WRITE_EXTERNAL_STORAGE(官方建议全部申请,有助于推荐效果与 ecpm), 并注册了DJXHostActivity。上架前请把对应权限用途写进隐私政策。 - PrivacyInfo:iOS 侧已随
Resources/提供VeLive.bundle/RangersAPMPrivacyInfo.bundle等隐私清单, 请勿删除;App Store 提交时会读取。 - 调试建议:先用
createShortDrama({ debug: true })打开 SDK 日志,再结合onError/onLoad判断卡在哪一步。 -
uni-ad模块必须按平台分开配置(打包前务必确认):gm(穿山甲 GroMore)在 iOS 侧会把整套TTSDKFramework(播放器内核)拉进 app 工程, 与插件自带的那份同输出路径双写,云打包 archive 阶段报error: Multiple commands produce '.../UniAppX.app/Frameworks/<TTSDK 系>.framework'(一连 23 条)。 插件已自带广告与播放器依赖,正确写法是把两个平台分开:"app" : { "distribute": { "modules": { "uni-ad": { "gdt": {}, "ks": {} } } } }, "app-android" : { "distribute": { "modules": { "uni-ad": { "gm": {}, "gdt": {}, "ks": {} } } } }, "app-ios" : { "distribute": { "modules": { "uni-ad": { "gdt": {}, "ks": {} } } } }- Android 必须保留
gm:插件启动时会反射TTAdSdk初始化广告 SDK,缺少该类会报-7004 TTAdSdk class not found。 - iOS 必须去掉
gm:广告 SDK(Ads-CN)与播放器(TTSDKFramework)均已由插件自身提供,去掉后功能不受影响。 - 若你的业务另外用到 uni-ad 的穿山甲(GroMore)渠道广告位,注意 iOS 侧将不可用; 仅用插件内的短剧解锁广告则无影响。
- Android 必须保留
常见报错
| 错误码 | 原因 | 处理 |
|---|---|---|
04016 |
包名 / Bundle ID 与后台不匹配 | 替换 SDK_Setting.json,核对 license_config[].PackageName / BundleId 与工程一致 |
04023 |
sha1 不匹配 | 签名与后台录入的不一致;debug / release 签名需统一 |
04010 |
时间戳异常 | 设备时间被手动修改 |
04005 |
短剧不存在 | 内容已下线,实时拉取,不要缓存渲染 |
error_code: 2广告sdk未初始化成功 |
① 未勾选穿山甲广告模块;② iOS 上 CSJAdSDK.bundle 版本与本次链接的广告 SDK 不一致 |
① 勾选 uni-ad 穿山甲模块;② 跑 scripts/verify-ios-resources.sh 校验并同步资源 |
-1 |
参数错误,或短剧 SDK 尚未启动完成(Android) | 启动中的调用稍后重试;建议在 App 启动时先 createShortDrama() |
-3 |
iOS 端点赞 / 收藏接口未公开 | 业务侧自行维护点赞、收藏状态 |
-5 |
getInfo() 未提供 dramaId 或 dramaIds |
至少传一个 |
-1001 |
iOS 未链接到 PangrowthDJX(pod 未装成功) |
检查 utssdk/app-ios/config.json 的 PangrowthX/shortplay 是否解析成功 |
-1002 |
找不到 iOS 配置文件 / 内容为空 | 确认 Resources/SDK_Setting.json 存在且非空 |
-1003 |
iOS 配置文件不是合法 JSON | 用后台下载的原文件替换,不要手工改格式 |
-7001 |
广告 SDK appId(init.site_id)读取失败 |
检查 assets/SDK_Setting.json 的 init.site_id |
-7002 / -7003 |
广告 SDK 的 init / start 方法不存在 |
广告 SDK 版本异常,检查 uni-ad 模块是否勾选 |
-7004 |
找不到 TTAdSdk 类 |
未勾选穿山甲广告模块;在 manifest.json 勾选 uni-ad 后重打基座 |
-7005 / -7006 |
反射初始化广告 SDK 抛异常 | 查看 logcat 中原生堆栈,通常是广告模块版本冲突 |
-7007 |
广告 SDK start 回调失败 |
检查媒体 ID、网络、以及后台是否开通短剧能力 |
常见问题
Q:插件提供列表 UI 吗? 不提供。插件只有 API 与三个原生页面(播放页、滑滑流、聚合页),列表、搜索、收藏等页面需要业务侧自己写。
Q:默认 'sdk' 解锁需要传激励视频广告位 ID 吗?
不需要。广告位来自 SDK_Setting.json 的后台配置。只有 'custom' 模式才需要业务侧自己 uni.createRewardedVideoAd({ adpid })。
Q:'sdk' 和 'custom' 怎么选?
'sdk' 接入最简单,广告请求完全交给短剧 SDK;'custom' 适合已有自己广告体系(UniAd / 自有聚合)的业务,
但对应的瀑布流必须包含穿山甲广告,并且要完整回传展示与解锁结果,否则会被强制切回 SDK 直出广告。
Q:为什么调 API 立刻返回 -1?
Android 上广告 SDK 尚未初始化完成(通常几秒)。插件已自动排队启动,稍后重试即可;
更稳妥的做法是在 App 启动时就 createShortDrama(),把启动时间提前。
Q:重装应用后为什么还保留解锁记录?
未绑定业务账号时,解锁记录与设备关联;调用 bindUnlockRecord() 绑定账号后,解锁权益跟随账号,
账号切换 / 退出登录由业务侧负责解绑与重绑。
Q:iOS 上点赞 / 收藏没有反应?
iOS 原生未公开这两个接口,插件返回 -3,请在业务侧维护状态。
Q:iOS 播放页一打开就闪退,崩溃日志里有 MTLReportFailure / MTLLibraryBuilder?
App 包根目录缺少播放器的 Metal 着色器(*.metallib)。确认 utssdk/app-ios/Resources/ 下的 *.metallib 没有被删,
然后重新制作自定义基座。详见 iOS 资源版本一致性。
Q:Android 运行时莫名报错? 不要勾选 HBuilderX 的「清理构建缓存」,并在改完插件后重新制作自定义基座。
Q:页面销毁时要做些什么?
调用 shortDrama.destroy() 清空事件监听,并把业务侧持有的 shortDrama 置空;destroy() 不负责解绑账号。
iOS 资源版本一致性(进阶)
这是本插件在 iOS 上最容易踩的坑,正式上线前请花 10 秒跑一次校验。
背景:短剧播放器与广告 SDK 在运行时会优先到 App 包根目录(Bundle.main) 找自己的资源 bundle。
插件的 utssdk/app-ios/Resources/ 会被 HBuilderX 平铺拷贝到 .app 根目录,因此这里的资源版本必须与
本次打包实际链接的 SDK 版本一致:
CSJAdSDK.bundle/version.txt与链接进产物的广告 SDK 版本不一致 → 广告 SDK 初始化失败 → 内容 SDK 报error_code=2;*.metallib缺失或版本不匹配 → 一进播放器SIGABRT(MTLReportFailure)。
为什么会漂:PangrowthX/shortplay 只写了 Ads-CN/BUAdSDK >= 5.8.0.9,没有锁死版本,
CocoaPods 每次打包会取「当时最新」的 Ads-CN,而插件自带的 bundle 是固定的。
校验方法(每次重新打自定义基座后执行):
cd uni_modules/csj-short-drama
./scripts/verify-ios-resources.sh # 默认读 ../../unpackage/debug/iOS_debug_vapor.ipa
./scripts/verify-ios-resources.sh /path/to/your.ipa # 也可以指定 ipa
脚本会把 Resources/ 里每个文件与 ipa 内插件 framework 中的同名资源逐字节比对:
✓一致;✗ 版本不一致→ 用本次构建产物(Payload/*.app/Frameworks/unimodule*.framework/) 里的同名资源覆盖Resources/,再重新打包;⚠ 构建产物里有、插件 Resources 没有→ 该资源会缺失在 App 根目录,按需补进Resources/;- 退出码非 0 表示校验失败(可接入 CI)。
⛔ 不要从别的工程、别的 pod 版本或 CocoaPods 缓存里随手拷
CSJAdSDK.bundle/*.metallib—— 版本对不上比不拷更糟。唯一正确的来源是本工程这一次的构建产物。
更新日志
v1.0.0(2026-09-29)
- 首个版本:Android / iOS 双端完整实现(列表、推荐、分类、搜索、详情、观看记录、收藏、分集解锁状态)。
- 三个原生页面:播放页
open()、滑滑流openDraw()、聚合页openHome()。 - 两种解锁模式:SDK 广告解锁与业务侧自定义广告解锁(
unlockStart/unlockRequired/unlockEnd三段事件)。 - 解锁记录绑定业务账号:
bindUnlockRecord()/unbindUnlockRecord()/isUnlockRecordBound()。 - 广告 SDK 由插件自动初始化(Android 反射
TTAdSdk、iOS 反射BUAdSDKManager,appId 取init.site_id, 分别带 15s / 10s 看门狗兜底),业务侧不必先调用 UniAd 广告 API。 - iOS 启动中的调用自动排队补跑;Android 先自动重排启动并提示稍后重试。
- iOS 侧随插件提供播放器 / 广告 SDK 所需的全部资源(
*.metallib、DJXSDK.bundle、CSJAdSDK.bundle等), 并提供scripts/verify-ios-resources.sh做版本一致性校验。
示例工程
pages/ 下提供 4 个可直接运行的场景页,覆盖主要接法:
| 页面 | 场景 |
|---|---|
pages/index/index.uvue |
示例导航页(原生聚合页 / 自建聚合页 / 全屏滑滑流 三个入口) |
pages/sdk-internal/index.uvue |
原生聚合页 openHome() + SDK / 自定义解锁切换 |
pages/api-list/index.uvue |
自建聚合页:列表 / 分类 / 搜索 / 收藏 / 观看记录 / 绑定解锁记录 / 打开播放页 |
pages/custom-ad/index.uvue |
全屏滑滑流 openDraw() + 自定义激励视频解锁完整流程 |
场景页采用统一的自定义视觉规范(深色面板 + 描边式选项组,每页一套主题色), 仅用于演示 API 调用方式。页面结构与样式同插件功能完全解耦, 业务侧可整体替换为自己的设计规范,只需保留
createShortDrama()的调用与事件绑定。示例中的激励视频
adpid、SDK_Setting.json均为示例应用配置,正式接入请全部替换成自己后台的配置。
相关文件
- API 契约(类型定义):
utssdk/interface.uts - iOS 资源一致性校验脚本:
scripts/verify-ios-resources.sh - 双端原生实现:
utssdk/app-android/(Kotlin)、utssdk/app-ios/(Swift)

收藏人数:
购买普通授权版(
试用
使用 HBuilderX 导入示例项目
赞赏(0)
下载 7
赞赏 0
下载 12649129
赞赏 1953
赞赏
京公网安备:11010802035340号