# 短剧播放器

## 一、介绍

短剧播放器是微信官方为微短剧类目小程序提供的播放器插件，旨在帮助短剧开发者降低开发及运营成本，提供短剧行业的运营组件能力，提升微信内短剧播放质量及优化用户体验。
开发者可引入短剧播放器插件后，基于自身需求进行开发配置。

**具体介绍**：[短剧播放器介绍](https://docs.qq.com/slide/DRWp3Zk1RWWdWTllt)

**接入示例**：[代码示例](https://developers.weixin.qq.com/s/aswCzvmR8Q89)

## 二、接入流程

为了更好地使用短剧播放器插件，您需要按照以下步骤进行接入：

1. 1.剧集内容授权：播放方小程序需要获得提审方剧集的授权，详见：[剧目授权](mini-drama/mini_drama#剧目授权)、[账号授权](mini-drama/mini_drama#账号授权)
2. 2.申请添加插件：接入短剧播放器插件之前，您需要先完成插件的添加申请。

- •【方式 1】页面操作添加：操作路径 **MP-设置-第三方设置-插件管理-添加插件**
> ![image.png](https://res.wx.qq.com/op_res/rNyJtB2xfKnkQpYg2zuNgVFAXDsg0QiC9QdBjgR1ftadVqGzRySq_C49w9S4dUOiF0I9PJbubDewCuVnlH5iQA)
> 
> 输入插件appid:wx94a6522b1d640c3b,即可添加完成。
> 
> ![image.png](https://res.wx.qq.com/op_res/rNyJtB2xfKnkQpYg2zuNgSv02tJlXiwLWhfhHZ5iRxpLoyJVTVIeogdZgvtdxUQ0wc7XzCxjaNGHVSYYrWF3tA)

- •【方式 2】开发者工具添加：详见 3.1 插件如何引入

1. 3.插件接入：开始进行插件的接入开发，详见文档 3.基础接入
2. 4.其他自定义开发：除了基本的插件接入，您还可以根据自身需求接入播放器所提供的运营能力以及进行其他自定义开发，为用户提供更多的功能和特性。详见文档 4.运营能力

## 三、基础接入

在接入之前，可以先提前了解下面的问题：

1. 1.在开发者工具调试时，需要先去掉 `app.json` 的设置`"lazyCodeLoading": "requiredComponents"`，否则可能会碰到黑屏，或者是报错`Plugin module "__wx__/plugin-private-api" is not defined.`。
2. 2.有些使用框架的开发者，例如uniapp，如果碰到自定义运营区域组件，或者充值组件无法渲染的问题，可能是该组件被框架删除了。可通过创建一个空的页面，去引用对应的组件，页面的json文件可参考

   ```json
    {
      "usingComponents": {
        "charge-dialog": "/components/charge-dialog/charge-dialog",
        "open-area-left": "/components/open-area-left/open-area-left",
        "open-area-left-side": "/components/open-area-left-side/open-area-left-side",
      }
    }
    ```

## 3.1 插件如何引入

首先在小程序的 app.json 文件中添加插件的引入配置，示例代码如下：

```json
{
  "plugins": {
    "playlet-plugin": {
      "version": "latest",
      "provider": "wx94a6522b1d640c3b"
    }
  }
}
```
"provider"字段为插件提供方的appid，设置为"wx94a6522b1d640c3b"即可。请确保将 "version" 字段设置为 "latest"，以使用最新版本的插件。

![image.png](https://res.wx.qq.com/op_res/qz5adP_wJ2mYx6Z3FW6p0YbHnPbudV3jCKA8EuGWKaOcNCLCJsfzjaQkTySWrruwK-pdvLth_HhwR4HWNSo_ww)

在开发者工具Console里点击「添加插件」即可自动通过。

具体的接入流程可参考下面的步骤：

### 3.2 插件接入流程

下面的接入流程涉及的一些字段的含义，先提前说明下：

- •`srcAppid`: 提审方 appid，通过小程序[媒资管理](mini-drama/mini_drama)上传了剧目的账号 appid。
- •`dramaId`: 剧目 ID，通过小程序[媒资管理](mini-drama/mini_drama)提审的剧目 id。
- •`serialNo`: 剧集 ID，从1开始递增，比如第 1 集则 serialNo 是 1，注意这里跟媒资管理的 mediaNo 是不一样的。

插件提供的能力说明：

- •插件提供了播放器页面，接入后可以通过播放器页面的路径使用，播放器的路径前缀为 `plugin-private://wx94a6522b1d640c3b/pages/playlet/playlet`。
- •播放器页面和小程序之间有比较多的逻辑交互，例如某一集是否能解锁观看，需要开发者提供，插件提供了一些接口来让小程序去交互。接口通过 `const playletPlugin = requirePlugin('playlet-plugin')` 返回的插件实例来调用。
- •点击可查看详细 [接口文档](videoplayer-interface)

播放器里面是否可解锁等数据，需要开发者提供，开发者可以参考下面的步骤进行接入。

#### 3.2.1 跳转到播放器页面

在开发者的页面中跳转到播放器页面，请参考以下示例代码：

```javascript
Page({
  onJumpPlayer() {
    const { srcAppid, dramaId } = this.data
    const extParam = encodeURIComponent('a=b&c=d') // 需要 encodeURIComponent

    // 跳转到短剧播放页
    wx.navigateTo({
      url: `plugin-private://wx94a6522b1d640c3b/pages/playlet/playlet?dramaId=${dramaId}&srcAppid=${srcAppid}&serialNo=${serialNo}&extParam=${extParam}`
    })
  },
})
```

播放器页面接收 srcAppid 参数、dramaId 参数和 serialNo 等参数，其中：

* dramaId 是开发者通过媒资管理的 API 上传的剧目 id，必填
* srcAppid 是提审方的 appid，必填
* serialNo 是指定播放的剧集id，从 1 开始递增（不是媒资管理的 mediaNo），选填，如果不传，进入播放器页面则会从上次播放的剧集开始继续播放
* extParam 是可选的扩展参数，选填，PlayletManager.getInfo() 可获得传入的此参数，分享的时候也会带上此参数，可以作为上报的参数使用。

当进入播放页时，会触发插件的 onPageLoad 回调给开发者。

#### 3.2.2 监听播放页加载

```javascript
const playletPlugin = requirePlugin('playlet-plugin')

App({
  onLaunch() {
    playletPlugin.onPageLoad(info => {
      const pm = playletPlugin.PlayletManager.getPageManager(info.playerId)

      // 初始化剧集列表状态
      pm.setCanPlaySerialList({
        serialList: [{
          start_serial_no: 1,
          end_serial_no: 4,
          status: 1, // 1-4 集解锁
        }, {
          start_serial_no: 5,
          end_serial_no: 20,
          status: 2, // 5 - 20 集未解锁
        }],
        freeList: [], // 免费剧集
      })

      // 用户进入未解锁的剧集时会触发此事件
      pm.onCheckIsCanPlay(param => {
        const serialNo = param.serialNo
        if (serialNo <= 10) {
          // 10 集以内解锁
          pm.isCanPlay({
            serialNo,
            serialList: [{
              start_serial_no: serialNo,
              end_serial_no: serialNo,
              status: 1,
            }],
          })
        } else {
          // 其他剧集不解锁 并展示支付弹窗
          pm.isCanPlay({
            serialNo,
            serialList: [{
              start_serial_no: serialNo,
              end_serial_no: serialNo,
              status: 2,
            }],
          })
          pm.showChargeDialog()
        }
      })
    })
  },
})
```

#### 3.2.3 实现后台接口初始化剧集状态

在上述代码中，setCanPlaySerialList({ serialList, freeList }) 接口来初始化剧集状态。

其中 serialList 是剧集解锁状态列表，类型为 `Array<SerialItem>`，SerialItem 定义如下：

| 字段名 | 类型 | 必填 | 描述 |
|:--:|:--:|:--:|:--|
| start_serial_no | number | 是 | 起始免费剧集编号，从 1 开始 |
| end_serial_no | number | 是 | 结束免费剧集编号，包含此集 |
| status | number | 是 | 上面的起始和结束区间内剧集是否解锁；1 - 已解锁，2 - 未解锁 |

freeList 参数是免费剧集列表，类型为 `Array<FreeItem>`，FreeItem 定义如下：

| 字段名 | 类型 | 必填 | 描述 |
|:--:|:--:|:--:|:--|
| start_serial_no | number | 是 | 起始免费剧集编号，从 1 开始 |
| end_serial_no | number | 是 | 结束免费剧集编号，包含此集 |

> 注意：免费剧集配置与“微短剧”优质小程序流量主激励政策有重要关联；它是剧集的一个特殊状态，和普通的剧集只有 ui 上的不同。免费剧集的 ui 如下：

![image.png](https://res.wx.qq.com/op_res/qz5adP_wJ2mYx6Z3FW6p0YkUglF3FHAS5CmH_UQvTJ1dd-wEYtTJMAEUflmQQZQPGaxXwXXwLcjtsfnSdjln9A)

#### 3.2.4 解锁剧集

当用户进入到未解锁的剧集时，会触发开发者使用 `onCheckIsCanPlay` 方法注册的回调函数。开发者需要在此回调函数中判断用户是否可以解锁该集。

开发者可以调用 `isCanPlay({ serialNo, serialList })` 方法来设置该集的解锁状态，同时消除播放器的 loading。此处的 `serialList` 定义和 `setCanPlaySerialList` 的 `serialList` 参数一致。

开发者将该集设置为解锁后，播放器便会自动开始播放该集内容；如果开发者将该集仍然设置为未解锁，播放器便会显示相应的未解锁界面，提示用户需要解锁才能继续观看。

![image.png](https://res.wx.qq.com/op_res/qz5adP_wJ2mYx6Z3FW6p0QK8J0nrkWzWUBrkBWOxNUn1_in-tNJbb7_xVVFq1LDuHX_8JNZd0yMBvgEdBIzpwg)

#### 3.2.5 充值组件

充值组件是一个自定义组件，会渲染在播放页面的半屏弹窗中。开发者需要自己实现此组件的充值逻辑，充值组件存在最大高度限制，如果内容高度超过弹窗高度，需使用 scroll-view 组件实现滚动。开发者需提前获取到渲染弹窗内容所需要的数据，避免在弹窗拉起的时候内容变化导致弹窗高度发生变化。

![image.png](https://res.wx.qq.com/op_res/qz5adP_wJ2mYx6Z3FW6p0fS41mCOyKdesuC3XMQulMI40GEF80ipt7niqtNF1SrWnriNor-OprUO3LPFJ6UAFA)

1. 1.在 app.json 文件中，需要定义组件 charge-dialog，指向开发者自己实现的充值组件。示例代码如下：

    ```json
    {
      "usingComponents": {
          "charge-dialog": "/components/charge-dialog/charge-dialog"
      },
      "plugins": {
        "playlet-plugin": {
          "version": "latest",
          "provider": "wx94a6522b1d640c3b",
          "genericsImplementation": {
            "playlet": {
              "charge-dialog": "/components/charge-dialog/charge-dialog"
            }
          }
        }
      }
    }
    ```

    > 注意：如果是在分包引入插件，上面定义的充值组件也需要在分包内。

1. 2.开发者可以通过 `showChargeDialog()` 方法来显示充值弹窗，并通过 `onShowChargeDialog(func)` 以及 `onHideChargeDialog(func)` 方法注册显示/隐藏弹窗的事件回调函数。

充值完成后，开发者 `onShowChargeDialog(func)` 方法注册显示弹窗的事件回调函数。当充值弹窗拉起时，会触发此回调函数。

1. 3.可以调用 `hideChargeDialog()` 来关闭弹窗。
2. 4.可以调用 `updateChargeDialogHeight({ height: 500 })` 来设置弹窗高度。
3. 5.可以调用 `updateChargeDialogInfo({ isTransparentBackground: true })` 来设置充值弹窗背景透明。
4. 6.播放器会将 `srcAppid`、`dramaId`、`serialNo`、`playerId`、`extParam` 作为属性传递给 `charge-dialog` 组件。开发者可以在组件中定义这些属性，并根据 serialNo 的变化，获取不同的充值数据进行渲染。

    ```javascript
    Component({
      properties: {
        playerId: {
          type: String,
          value: ''
        },
        srcAppid: {
          type: String,
          value: ''
        },
        dramaId: {
          type: String,
          value: ''
        },
        serialNo: {
          type: Number,
          value: 0
        },
        extParam: {
          type: String,
          value: ''
        },
      },
      observers: {
        serialNo(serialNo) {
          // 当集数发生改变的时候，触发此函数
        }
      }
    })
    ```

1. 7.事件系统：充值组件是在播放器页面引入的，开发者无法通过在 wxml 里面注册相关的事件，因此插件提供了事件函数，可使用下面的 onCustomEvent 注册事件，emitCustomEvent 触发事件

    ```javascript
    // 在充值组件内触发自定义事件
    pm.emitCustomEvent(name, args)

    // 在其他地方监听充值组件触发的自定义事件
    pm.onCustomEvent(name, func)
    ```

#### 3.2.7 数据加密方案

上述例子中的 setCanPlaySerialList 和 isCanPlay 方法的调用都是直接传递剧集解锁信息给播放器来实现剧集解锁状态的变更。如果开发者对剧集解锁状态的变更有更高的安全性要求且能承担**一定的性能损失**，还可以选用传递加密数据的方式来实现剧集解锁。

> 注意：选用加密方案的话安全性相对较高，但性能会有所损耗，可根据实际情况选用对应方案。

具体调用方式如下：

```javascript
// 无需加密方案
pm.setCanPlaySerialList({
  serialList: [{
    start_serial_no: 1,
    end_serial_no: 4,
    status: 1, // 解锁
  }, {
    start_serial_no: 5,
    end_serial_no: 20,
    status: 2, // 未解锁
  }],
  freeList: [],
})
pm.onCheckIsCanPlay(param => {
  pm.isCanPlay({
    serialNo: 15,
    serialList: [{
      start_serial_no: 15,
      end_serial_no: 15,
      status: 1, // 解锁
    }],
  })
})

// 加密方案
const data = await getEncryptData() // getEncryptData 需要开发者自行实现
pm.setCanPlaySerialList({
  data,
  freeList: [],
})
pm.onCheckIsCanPlay(async param => {
  const data = await getEncryptData() // getEncryptData 需要开发者自行实现
  pm.isCanPlay({
    serialNo: 15,
    data,
  })
})
```

其中 data 参数是加密后的数据，即 LockDataReq 对象经过 JSON.stringify，然后再加密后得到的字符串。LockDataReq 定义如下：

| 参数名 | 类型 | 描述 |
|:--:|:--:|:--|
| openid | string | 当前观看用户的openid |
| src_appid | string | 提审方小程序的appid |
| drama_id | string | 提审的剧目id |
| serial_list | `Array<SerialItem>` | SerialItem 数组，SerialItem 定义同上 |
| data_expire_at | number | 数据有效期，时间戳（秒）。到时间过期不可用，建议当前时间戳 +2 小时 |

> 注意：接入了免开发激励视频广告解锁必须采用加密方案来调用 setCanPlaySerialList 和 isCanPlay 方法。

##### 具体加密流程

上述加密方案中的加密数据需要经过在开发者后台使用 session_key 的前16个字节作为密钥加密（不足 16 个字节后面补零），加密算法 AES-128-ECB。

加密步骤：

1. 1.调用微信 api 获取当前登录用户的最新 session_key

![image.png](https://res.wx.qq.com/op_res/qz5adP_wJ2mYx6Z3FW6p0fK8lvPkPC_9S_MGJsIRcqcLLswrg4NmYx1bdmGeyW7w2Gd1lVBKVv30ymr5gJLKBQ)

1. 2.使用 LockDataReq 结构组装待加密的明文数据（LockDataReq 定义见上述介绍）
2. 3.将 LockDataReq 转成 json 字符串 data
3. 4.使用 AES-128-ECB 算法对 data 进行加密，密钥是第 1 步获取的 session_key 的前 16 个字节
4. 5.对 data 加密后的密文进行 base64 编码

示例：

1. 1.LockDataReq 转成 json 的示例（以下示例表示解锁第 10-12 集,不解锁第 13-15 集）

```json
{"openid":"xJad239kjafkjakdjfe","src_appid":"wx98293892389823","drama_id":"21212","serial_list":[{ "start_serial_no":10,"end_serial_no":12,"status":1},{"start_serial_no":13,"end_serial_no":15,"status":2}], "data_expire_at":1703474855}
```

1. 2.加密示例

密钥是 `89283sdkj1212121212`，将上述示例加密后的 base64 编码结果：

```
LHb+riG/WsCP8wqQEIvJxCx59exL+Wjf23R/pTIbC9gZo3yrUrMMoqIb3/zviK0jtt2HBTk4Eke/bmSD/qKwfY277NK6YNv9wML8+loNnjIkPJHj3DHUALEcwxBTOUVqgTBNTFimtdU7qwkqMIMU1RSlQR+U9CFckbX46mCQoyA8VssxP5ej16+WIcnVqGbkZQdI5hoWZqvwRdnQtMV3s5ZDlGxxYS75XoTYMNdNYOS6bA9jt4K1AUWuEQcOw8ygs4a8EqYbwnA7uD6Ks+54O6xtEXmLyr5uPZKXcbyzjWs17+odJHI+e0gU5o79e3I6
```

> 开发者可以使用解密代码解密以上实例，验证加解密逻辑是否正确

## 四、其他开放接口

### 4.1 暂停/继续播放

`PlayletManager.play()` 和 `PlayletManager.pause()`

### 4.2 获取剧目信息

`getInfo()` 获取到当前剧的相关信息，具体返回的数据结构如下：

|参数名 |类型 |描述 |
|:--:|:--:|:--|
| srcAppid | string | 小程序 APPID |
| dramaId | string | 剧id |
| serialNo | number | 当前播放集id |
| extParam | string | 可选，传入的扩展信息 |

> PS：以上为固定常用字段，其他页面路径上的参数也会在此处返回。

### 4.3 更换剧目

```javascript
// 请将{srcAppid}替换为提审方appid，{dramaId}替换为真实剧id
PlayletManager.changeDrama({
    queryString: 'srcAppid={srcAppid}&dramaId={dramaId}'
})
```

### 4.4 数据上报

```javascript
PlayletManager.onDataReport(func)
```

开发者可通过此方法注册回调函数获得数据上报相关的回调，其中event表示上报的点，event是个枚举值，具体的取值如下。
每个上报事件，都会有一些公共的字段会带回给开发者：

| 字段名 | 描述 |
|--|--|
| playerId | 播放器id |
| srcAppid | 提审方appid |
| extParam | 分享扩展参数 |
| serialNo | 当前剧集索引，从1开始计数 |
| playTime |当前播放到的时间 |
| duration | 当前剧集的总时长 |
| totalCount | 当前剧集总集数 |

每个事件还有自己额外的一些字段，具体如下：

| 值 | 描述 | 额外字段说明 |
|--|--|--|
| LOAD | 进入播放器页面 | |
| SHOW | 播放器页面 show | |
| START_PLAY | 开始播放 | |
| UNLOAD_OR_HIDE | 离开页面或者退后台 | 例如 { type: 1 }。type 为 1 表示Unload，为 2 表示 hide |
| VIDEO_END | 播放视频结束 | |
| VIDEO_START_WAITING | 视频缓冲中，对应 video 的 waiting 事件 | |
| VIDEO_WAITING | 视频缓冲结束，stopTime 表示 VIDEO_START_WAITING 到首个 VIDEO_TIME_UPDATE 的时间 | 例如 { stopTime: 0 }。stopTime表示缓冲了多久才继续播放，单位 ms |
| VIDEO_ERROR | 视频播放失败 | 例如 { error: '' }。error表示错误信息 |
| VIDEO_REMOVED | 该剧已下架 | |
| CHANGE_SERIAL | 切换剧集 | 例如 { isFinish: true, slideType: 1 }。isFinish 为 true 表示是播放结束自动切换到下一集。slideType 为 1 表示上一集播放结束自动切换，2 表示手势滑动切换，3 表示从选集弹窗切换 |
| CLICK_CHANGE_SERIAL | 点击拉起切换剧集弹窗 | |
| LIKE | 点赞 | |
| UNLIKE | 取消点赞 | 例如 { type: 2 }。type 为 2 表示双击屏幕点赞, 1 表示点击 icon 点赞 |
| HEART | 推荐 | |
| UNHEART | 取消推荐 | |
| SHARE | 分享 | 例如 { shareType: 1, shareId: '' }。shareType 为 1 表示分享好友，2 表示分享海报。shareId 表示每次分享唯一的字符串，会在分享的 path 上带出去 |
| CLICK_ACTIVITY | 点击活动位 | |
| CHANGE_SPEED | 倍速播放 | { source: 1, speedType: 1.5 }。source 为 1 表示点击倍速按钮切换，2 表示长按视频切换到 2 倍速，speedType 表示切换到的倍速，例如 1.5 倍速 |
| VIDEO_PROGRESS | progress 事件 | 参数参考 video 组件的 progress 事件 |
| VIDEO_TIME_UPDATE | timeupdate 事件 | 参数参考 video 组件的 timeupdate 事件 |
| VIDEO_PLAY | play 事件 | |
| VIDEO_PAUSE | 暂停事件 | reason 表示暂停原因：1 - 拖动进度条；2 - 点击视频；3 - 展示分享海报；4 - 调用 pause 接口；5 - 点击横屏控制栏暂停按钮 |
| REQUEST_ERROR | 插件内部请求接口报错，ret 表示错误码 | { ret: 21003 }，ret 为 1 表示 dramaId 或 srcAppid 填错，21003 表示解密失败 |
| ADD_FOLLOW_SUC | 关注公众号 | |
| OPEN_OFFICIAL_ACCOUNT | 打开公众号 | |
| WATCH_TIME | 观看时长 | { type: 1, playTime: 10, duration: 65 }。type 表示触发的时机，目前取值 1 表示 unload，2 表示 hide，3 表示某一集结束，4 表示用户手动拖动进度条（已废弃，被包含在剧集暂停之中），5 表示切换了剧集，6 表示剧集暂停；playTime 是此次观看时长 |
| TAP_UNLOCK | 点击解锁按钮 | |
| BACKGROUND_PREVIEW_PLAY | 剧集未解锁时的视频预览开始播放 | |
| BACKGROUND_PREVIEW_ENDED | 剧集未解锁时的视频预览播放结束 | |
| CLICK_RECOMMEND_TAB | 点击选集弹窗中的推荐 tab | |
| CHANGE_RECOMMEND_TAB | 选集弹窗汇总切到推荐 tab | 可通过点击切换或手势切换到推荐 tab，如果是点击切换到推荐 tab，会有额外的 CLICK_RECOMMEND_TAB 事件 |
| CLICK_SELECT_DRAMA_RECOMMEND | 点击选集弹窗中推荐 tab 里的剧目 | clickDramaid、clickDramaname、clickSrcAppid 表示被点击的剧目信息 |
| RECOMMEND_END_SHOW | 剧目最后一集播放结束后的推荐列表展示 | |
| CLICK_RECOMMEND_END | 点击剧目最后一集播放结束后的推荐列表里的剧目 | clickDramaid、clickDramaname、clickSrcAppid 表示被点击的剧目信息 |
| CLICK_DETAIL_ENTRY_ACTOR | 点击选集弹窗中的演员 | actorIndex 表示被点击的演员下标 |
| CLICK_DETAIL_ENTRY | 点击选集弹窗中的剧目详情入口 | |
| CLICK_DETAIL_DRAMA | 点击剧集详情页中的相关推荐剧目 | clickDramaid、clickDramaname、clickSrcAppid 表示被点击的剧目信息 |
| CLICK_PROFILE | 点击小程序 profile 页入口 | |
| CLICK_SETTINGS | 点击设置按钮 | |
| OPEN_CLEAR_SCREEN | 打开清屏开关 | |
| CLOSE_CLEAR_SCREEN | 关闭清屏开关 | |
| OPEN_BARRAGE | 打开弹幕 | |
| CLOSE_BARRAGE | 关闭弹幕 | |
| CLICK_BARRAGE_SETTINGS | 点击弹幕设置 | |
| CLICK_COMMENT_ENTRY | 点击评论按钮 | |
| FEEDS_AD_EXPOSE | 信息流广告曝光 | 此处的 serialNo 表示广告后一集 |
| AD_WILL_PLAY_TIPS_SHOW | 广告倒计时提示曝光 | |
| CLICK_CANCEL_AD_TIPS | 点击广告倒计时提示中的“暂不观看” | |
| DRAMA_STATUS_INVALID | 短剧特殊状态页曝光 | type 表示短剧的特殊状态：1 - 未授权；2 - 已下架 |
| SERIAL_LOCK | 短剧解锁页曝光 | |
| CLICK_LANDSCAPE_BUTTON | 点击横屏按钮。只有宽高比符合横屏视频标准的剧集才会显示此按钮 | |
| LANDSCAPE_BACK | 横屏情况下点击返回按钮 | |
| CLICK_LANDSCAPE_PLAY_BUTTON | 点击横屏中进度条左边的播放/暂停按钮 | type 表示操作类型，0 表示赞同，1 表示播放 |
| RESIZE | 触发转屏事件 | deviceOrientation 表示屏幕方向，值为 landscape/portrait |

每个event的取值，对应的参数也不一样。

### 4.5 自定义返回按钮

> 注意，不可以在onBack回调函数里面调用`wx.navigateBack`等路由接口，可改为navigator组件或下面的路由跳转的接口进行跳转

```javascript
PlayletManager.onBack(function() {
    // 在这里做一下挽留逻辑
    console.log('用户点击了返回按钮')
})
```

### 4.6 路由跳转

由于在插件页，小程序不能调用`wx.navigateTo`等接口，所以插件提供下面的接口进行跳转
注意：这些接口调用成功的前提是用户必须至少点击过页面。

```javascript
PlayletManager.navigateTo(obj) // 含义和参数同wx.navigateTo
PlayletManager.navigateBack(obj) // 含义和参数同wx.navigateBack
PlayletManager.redirectTo(obj) // 含义和参数同wx.redirectTo
PlayletManager.switchTab(obj) // 含义和参数同wx.switchTab
PlayletManager.reLaunch(obj) // 含义和参数同wx.reLaunch
```

### 4.7 防录屏/截屏

若不调用此接口，播放器默认设置visualEffect为hidden具体使用可[参考文档](/miniprogram/dev/api/device/screen/wx.setVisualEffectOnCapture.md)

```javascript
PlayletManager.setVisualEffectOnCapture({
  visualEffect: 'hidden',
})
```

### 4.8 设置免费剧集列表

开发者可以进入页面就通过 `setCanPlaySerialList(freeList: [])` 接口设置免费剧列表（只针对已解锁剧集生效），如果开发者需要在用户观看过程中重新设置免费剧列表，可通过setFreeList设置，注意：此接口仅改变目录中的剧集显示状态，并不会做真正的解锁处理。

```javascript
PlayletManager.setFreeList({
  list: [{ start_serial_no: 1, end_serial_no: 10 }], // 1~10集是免费剧集
})
```

### 4.9 设置用广告解锁剧集

开发者可以进入页面就通过 `setUseAdUnlock(list: [])` 接口设置需要广告解锁的剧集，设置后，解锁按钮的文案变为“看广告后播放”，点击后不会拉起充值弹窗，会回调给开发者事件。

> 注意：通过此接口设置的剧集，在选集界面上的 ui 表现会和免费剧集一样。

```javascript
PlayletManager.setUseAdUnlock({
  list: [{ start_serial_no: 79, end_serial_no: 80 }], // 79~80集会通过广告解锁
})
```

开发者可通过接口 `onUseAdUnlock` 注册点击按钮的事件，通过激励视频去解锁

```javascript
pm.onUseAdUnlock(function() {
  // 拉起激励视频广告去解锁
})
```

设置广告解锁后，当用户即将进入需要广告解锁的剧集时，会出现广告倒计时提示。此时开发者可在广告倒计时提示的曝光事件（即 `AD_WILL_PLAY_TIPS_SHOW` 事件）中提取拉取并初始化广告，然后在进入对应广告解锁剧集触发的 `checkIsCanPlay` 事件中会有 `needToCancelAdTimer` 字段为 `true`，表示当前用户已经看到了对应当前剧集的广告倒计时提示，可以不用再显示广告倒计时弹窗。

如果用户在广告倒计时中点击了“暂不观看”，则会触发 `CLICK_CANCEL_AD_TIPS` 事件，表示用户暂时不想观看广告。此时进入到对应广告解锁剧集时不会触发 `checkIsCanPlay` 事件，并停留在该集的解锁页面。

### 4.10 设置剧集未解锁时的视频预览

开发者可以设置剧集未解锁时的视频预览。当用户进入未解锁剧集时，可以在展示解锁按钮的同时在后台播放剧集视频预览：

```js
// 播放预览
pm.startBackgroundPreview({
	loop: true, // 是否循环
	mask: true, // 是否遮罩
	muted: true, // 是否静音
})

// 停止预览播放
pm.stopBackgroundPreview()
```

> 注意：视频预览功能，会有泄漏播放链接的风险，且该能力会消耗商家流量，请按需使用。

### 4.11 退出横屏

在用户进入横屏之后，如果碰到需要充值的剧集，需要退出横屏的，可调用此接口。接口调用如下：

```javascript
pm.setPageOrientationPortrait()
```

### 4.12 播放原始视频

详情请查看：[播放原始视频（推荐位控制接口）](/miniprogram/dev/server/API/dramaOthersAPI/api_setplayerdramarecmdswitch.md)

### 4.13 最近观看组件

提供最近观看以供开发者在小程序中引用。在页面的 `page.json` 里面引入组件：

```json
{
  "usingComponents": {
    "recent-list": "plugin://playlet-plugin/recent-list"
  }
}
```

`recent-list`即是最近观看组件，在页面 `wxml` 中添加组件：

```html
<recent-list
    showHeader="{{true}}"
    bind:tapempty="onTapEmpty"
    bind:click="onClick"
    url="/pages/charge/charge"
></recent-list>
```

组件的使用和小程序组件类似，两个组件接收参数 `url`，`url` 表示列表为空的时候，点击“去看剧”跳转的链接，`tapempty` 事件表示点击了“去看剧”。

`click` 事件表示点击了对应的剧。

`showHeader` 属性表示是否显示组件的标题。

### 4.14 刷剧组件

提供刷剧组件以供开发者实现类似短剧广场等类似功能。在页面的 page.json 里引入组件：

```json
{
  "usingComponents": {
    "drama-feed-list": "plugin://playlet-plugin/drama-feed-list"
  }
}
```

在页面wxml中添加组件：

```html
<drama-feed-list
  id="drama-feed-list"
  autoplay
  bind:videoPlay="onVideoPlay"
  bind:videoPause="onVideoPause"
  bind:videoEnded="onVideoEnded"
  bind:videoError="onVideoError"
  bind:change="onChange"
  bind:watchFullDrama="onWatchFullDrama"
></drama-feed-list>
```

其中组件支持的参数如下：

| 参数名 | 类型 | 默认值 | 描述 |
|-|-|-|---|
| shareSrcAppid | string | | 置顶短剧的 srcAppid |
| shareDramaId | string | 置顶短剧的 dramaId |
| autoplay | boolean | false | 组件初创时是否需要自动播放 |
| mode | string | | 刷剧模式，默认为普通刷剧，可传入 `custom-list` 表示自定义刷剧 |
| custom-list | Array | [] | 当 mode 为 `custom-list` 时需传入，表示自定义的刷剧列表 |

组件支持的事件如下：

| 事件名 | 描述 |
|-|---|
| videoPlay | 视频播放事件 |
| videoPause | 视频暂停事件 |
| videoEnded | 视频播放结束 |
| videoError | 视频播放报错 |
| change | 切换视频 |
| watchFullDrama | 点击“看全集”按钮 |
| willReachEnd | 列表即将触底事件，当 mode 为 `custom-list` 时才会触发 |
| reachEnd | 列表触底事件 |

事件中的 detail 字段会带有下述参数：

| 参数名 | 类型 | 描述 |
|-|-|---|
| dramaId | string | 当前短剧的 dramaId |
| srcAppid | string | 当前短剧的 srcAppid |
| desc | string | 当前短剧的描述 |
| mediaCount | number | 当前短剧的总集数 |
| dramaName | string | 当前短剧的剧名 |
| playTime | number | 当前视频的播放时长 |
| duration | number | 当前视频的总时长 |
| err | Object | 错误对象，videoError 事件才会返回 |
| index | number | 当前短剧在列表中的下标 |
| direction | number | 划动方向，change 事件才会返回，1 表示向下划动，2 表示向上划动 |

如果需要控制刷剧组件的播放和暂停，可通过调用组件实例的方法实现：

```js
this.selectComponent('#drama-feed-list').play()
this.selectComponent('#drama-feed-list').pause()

// 当 mode 为 custom-list 的时候，还支持以下接口
this.selectComponent('#drama-feed-list').appendCustomList([{ dramaId: '', srcAppid: '' }])
this.selectComponent('#drama-feed-list').replaceCustomList([{ dramaId: '', srcAppid: '' }])
```

使用刷剧组件有以下几点需要注意：

1. 1.目前刷剧组件播放的都是短剧的第一集。
2. 2.当前视频播放完后，刷剧组件并不会切到下一集/下一部短剧，而是自动重播当前视频。
3. 3.点击“看全集”按钮时触发的 watchFullDrama 事件需要开发者处理，一般可处理为跳转到该短剧的实际播放页。

刷剧组件本身有两种模式，一种是普通模式，一种是自定义刷剧模式。其中普通模式为开发者通过下述后台接口设置刷剧剧目，然后刷剧组件会从后台拉取推荐列表进行展示；而自定义刷剧模式则完全由开发者提供刷剧列表，刷剧组件只补全对应的信息再进行展示。

1. 1.刷剧剧目设置接口：[刷剧剧目设置](/miniprogram/dev/server/API/dramaOthersAPI/api_developersetflushdrama.md)
2. 2.自定义刷剧模式：当组件属性 mode 传入 `custom-list` 字符串时表示切换到自定义刷剧模式，此时要求开发者必须传入 custom-list 属性：

    ```html

    ```

    其中 customList 的结构如下：

    ```js
    Component({
      data: {
        customList: [
          { dramaId: '', srcAppid: '' },
          { dramaId: '', srcAppid: '' },
          // ...
        ],
      },
    })
    ```

    因为短剧是通过 dramaId 和 srcAppid 来确定唯一一部剧，所以开发者只需要传入 dramaId 和 srcAppid 即可，刷剧组件会将这部剧的其余信息填补进去后进行展示。

    当短剧列表即将被划动到底部时，刷剧组件会触发 willReachEnd 事件，开发者可在此事件中进行列表补充：

    ```js
    // 此处传入的列表会追加到现有的刷剧列表后面
    this.selectComponent('#drama-feed-list').appendCustomList([
      { dramaId: '', srcAppid: '' },
      { dramaId: '', srcAppid: '' },
      // ...
    ])
    ```

    > 注意：刷剧组件里会将列表中每 3 项计为一组，willReachEnd 事件目前会在划到倒数第二组的时候触发。建议传入的自定义列表至少为两组以上。

    如果开发者想直接替换掉完整的刷剧列表，可以调用下述接口：

    ```js
    // 此处传入的列表会替换掉现有的刷剧列表
    this.selectComponent('#drama-feed-list').replaceCustomList([
      { dramaId: '', srcAppid: '' },
      { dramaId: '', srcAppid: '' },
      // ...
    ])
    ```

## 五、运营能力

完成播放器插件基础接入流程后，可根据业务需求按需使用相关运营能力。

### 5.1 分享

#### 5.1.1 分享开关

分享开关默认打开，您可以通过调用 PlayletManager 的 setDramaFlag 方法来打开或关闭分享开关。示例代码如下：

```javascript
PlayletManager.setDramaFlag({
    share: true,
    withShareTicket: true
})
```

> 请注意，若小程序本身的分享能力受限，则分享入口则只有分享海报功能。withShareTicket为true可获取更多信息，具体使用可参考[文档](/miniprogram/dev/framework/open-ability/share.md)。

#### 5.1.2 分享海报

播放器支持生成剧目海报。

![image.png](https://res.wx.qq.com/op_res/s4Gf6F6bJNM-SJCLjicKBahWrd6Yn7Ho7WPSaZXCoNvywotQ1sVg6JiBoFikSMR4S0GFqWEaLkqTLMp9A7t_bQ)

开发者需要先在MP完成隐私协议配置，否则将隐藏C端用户的海报生成入口。

**隐私协议配置**

路径：设置-基本设置-服务内容声明-用户隐私保护指引

![image.png](https://res.wx.qq.com/op_res/s4Gf6F6bJNM-SJCLjicKBQ1Y3EJFg6ZDU7jdYSmOYKCyj18keCtxHM09JOu5OkRqwXImjaC7k_hC4GPhy9Vbog)

点击“更新”/“去完善”，选择“相册（仅写入）权限”

![image.png](https://res.wx.qq.com/op_res/s4Gf6F6bJNM-SJCLjicKBZh-EjsqYfocwybX3qv_Per6CHjZoC13c5dfzjHa6eM0sc2QLGYZhnH4E_uk6W4ldQ)

![image.png](https://res.wx.qq.com/op_res/s4Gf6F6bJNM-SJCLjicKBbrsRJgQhp3Ud6QmI5VRY3aSIkzBu43jz2J3hUbXWn-RasNRjgy9wjogFL5GmdMfoQ)

#### 5.1.3 分享参数

您可以在 URL 中传入 `extParam` 参数，在分享时会将此参数带到分享出去的卡片上。有两种设置 `extParam` 的方式：

1. 1.在 `navigateTo` 跳转页面时，将此参数放到 URL 上。
2. 2.通过 `PlayletManager.setExtParam(extParam)` 设置。注意，我们会对此参数进行 `encodeURIComponent` 编码，因此在获取此参数时，开发者需要进行 `decodeURIComponent` 解码操作后再解析。

#### 5.1.4 shareId

每次分享都会在分享出去的小程序卡片或者小程序码的 `path` 里面带参数 `shareId`，而且此 `shareId` 会通过 `onDataReport` 事件报给开发者，当用户再从卡片进入播放器，开发者可通过 `getShareParams` 方法获得此参数，进一步关联之前分享的用户，做一些分享相关的运营活动。

```javascript
// 开启分享以及 withShareTicket
pm.setDramaFlag({
  share: true,
  withShareTicket: true
})

// 获取分享参数,页面栈只有短剧播放器一个页面的时候可获取到此参数
// 例如从分享卡片进入、从投流广告直接跳转到播放器页面，从二维码直接进入播放器页面等情况
playletPlugin.getShareParams().then(res => {
  console.log('getLaunch options query res', res)

  // 关于 extParam 的处理，需要先做 decodeURIComponent 之后才能得到原值
  const extParam = decodeURIComponent(res.extParam)
  console.log('getLaunch options extParam', extParam)

  // 如果设置了 withShareTicket 为true，可通过文档的方法获取更多信息
  // 1003848
  const enterOptions = wx.getEnterOptionsSync()
  console.log('getLaunch options shareTicket', enterOptions.shareTicket)
}).catch(err => {
  console.error('getLaunch options query err', err)
})

// 设置分享参数
pm.setExtParam('scene')
```

#### 5.1.5 分享组件

分享组件用于自定义一些分享信息，具体使用如下：

首先在组件中的 `json` 里引用分享组件：

```json
{
  "usingComponents": {
    "playlet-share": "plugin://playlet-plugin/share"
  }
}
```

在组件 `wxml` 中引入分享组件：

```html
<playlet-share
  imageUrl="{{imageUrl}}"
  title="{{title}}"
  shareExt="ext%3D1%26extb%3D2"
>
  <text>点击分享</text>
</playlet-share>
```

`imageUrl` 是分享卡片的图片，`title` 是分享卡片的标题，`shareExt` 是分享的自定义参数，会带在小程序的页面路径上。

### 5.2 运营位

在胶囊旁边显示“活动”运营位，icon可自定义。
可通过 PlayletManager 的 setActivityInfo(info) 来进行设置，具体的示例代码可参考

```javascript
pm.setActivityInfo({
  url: '', // 活动页面的路径，必填
  logo: '', // 显示的 icon 的 url
})
```

### 5.3 自定义开放区域

为了最大程度提高播放器的灵活性，目前开放了一些区域让开发者自定义，可开放的的区域如下图所示：

![image.png](https://res.wx.qq.com/op_res/JexfFJndbjLMAEuxoC4DS7S-rlto6Hi7l-LHbf49S8YOUMJSyGf1N-BenL2E_IxjNOwK30OQcTMDWa_bJnK_AQ)

> 注意：open-area-left-side 和 open-area-left 的区域是一样的，均如上图所述，其中 open-area-left-side 可设置多个实例，且可定义每个组件实例的宽高和位置。

具体接入类似充值弹窗的内容，首先在 app.json 内注册组件，如下所示

```javascript
{
  "usingComponents": {
    "charge-dialog": "/components/charge-dialog/charge-dialog",
    "open-area-left": "/components/open-area-left/open-area-left",
    "open-area-left-side": "/components/open-area-left-side/open-area-left-side",
  },
  "plugins": {
    "playlet-plugin": {
      "version": "latest",
      "provider": "wx94a6522b1d640c3b",
      "genericsImplementation": {
        "playlet": {
          "charge-dialog": "/components/charge-dialog/charge-dialog",
          "open-area-left": "/components/open-area-left/open-area-left",
          "open-area-left-side": "/components/open-area-left-side/open-area-left-side",
        }
      }
    }
  }
}
```

open-area-left、open-area-left-side 是开发者需要实现的组件，当需要展示这几个组件的时候，可调用 PlayletManager 的 updateOpenArea 方法来显示/隐藏组件：

```javascript
pm.updateOpenArea({
  showLeft: true, // 显示 open-area-left 组件
  leftWidth: 72, // 宽度，超出截断
  leftHeight: 100, // 高度，超出截断

  // 显示 open-area-left-side 组件
  leftsideAreaList: [{
    // left、top、width、height 定义每个元素的位置
    left: 10,
    top: 100,
    width: 400,
    height: 172,

    // 同时可以透传一些字段给组件内部
    xxx: '',
  }],
  ext: '', // 如果需要统一额外传一些数据给组件，可通过此参数传入
})
```

以下为组件的实现参考：

```javascript
// open-area-left 组件
Component({
  properties: {
    playerId: {
      type: String,
      value: '',
    },
    serialNo: {
      type: Number,
      value: 0,
    },
    ext: {
      type: String,
      value: '',
    },
  },
})

// open-area-left-side 组件
Component({
  properties: {
    playerId: {
      type: String,
      value: ''
    },
    serialNo: {
      type: Number,
      value: 0
    },
    ext: {
      type: String,
      value: ''
    },
    item: { // 对应的 leftsideAreaList 数组中的某一项
      type: Object,
      value: {},
    },
    index: { // 对应的 leftsideAreaList 数组中的下标
      type: Number,
      value: 0,
    },
  },
})
```

### 5.4 公众号联动

详情可见[能力介绍](https://docs.qq.com/doc/DRVZ2V0lrSHFqZndH)

### 5.5 推荐位

播放器插件在不同场景提供推荐位以供开发者使用，推荐位默认关闭，可通过推荐位控制接口打开推荐位。

注意：推荐剧库大于等于 6 部时，才会在相应场景中出现推荐位。

![image.png](https://res.wx.qq.com/op_res/DC_6C3pT-tDIfA08WzaR84gENUhFBu_wMQu0CMxzNOi6MutopH2Kws1B3VXxpSALCTPenFpcnIp-vfCwMLHgmQ)

#### 5.5.1 推荐位控制接口

详情请查看 [播放原始视频（推荐位控制接口）](/miniprogram/dev/server/API/dramaOthersAPI/api_setplayerdramarecmdswitch.md)

#### 5.5.2 推荐剧目设置

详情请查看 [推荐剧目](/miniprogram/dev/server/API/dramaOthersAPI/api_developersetrecmddrama.md)

### 5.6 短剧上架

1. 1.短剧上架设置接口：[短剧上架](/miniprogram/dev/server/API/dramaOthersAPI/api_developerpublishdrama.md)
2. 2.获取已上架短剧：[获取已上架短剧](/miniprogram/dev/server/API/dramaOthersAPI/api_developergetpublisheddrama.md)

### 5.7 短剧变现类型

1. 1.设置短剧变现类型：[设置短剧变现类型](/miniprogram/dev/server/API/dramaOthersAPI/api_developersetiaadrama.md)
2. 2.获取短剧变现类型：[获取短剧变现类型](/miniprogram/dev/server/API/dramaOthersAPI/api_developergetiaadrama.md)

### 5.8 批处理短剧合作推广计划

详情请查看 [批处理短剧合作推广计划](/miniprogram/dev/server/API/dramaOthersAPI/api_batchprocessdramapromotion.md)

### 5.9 获取短剧合作推广活动

详情请查看 [获取短剧合作推广活动](/miniprogram/dev/server/API/dramaOthersAPI/api_getfinderevent.md)

## 六、常见问题

### 6.1 基础库兼容问题

播放器功能支持基础库版本建议为 3.1.5 及以上。

### 6.2 测试注意

1. 1.多种进入播放器的场景测试：开发者需要测试不同的场景，例如从首页跳转 `navigateTo/redirectTo`、从分享卡片进入，以及从其他小程序通过 `navigateToMiniProgram` 跳转等情况。确保在各种场景下播放器功能正常运行。
2. 2.关于 `lazyCodeLoading` 的注意事项：如果开启了 `lazyCodeLoading` 功能，需要注意在开发者工具中进行调试时可能会遇到 bug。在开发者工具中，需要先关闭以下设置：

    ```javascript
    {
        "lazyCodeLoading":"requiredComponents"
    }
    ```

### 6.3 投放路径设置

**投放的页面路径**

`plugin-private://wx94a6522b1d640c3b/pages/playlet/playlet?dramaId={dramaId}&srcAppid={srcAppid}&extParam={extParam}`

其中 `{dramaId}` 表示剧集 id，`{srcAppid}` 提审方 `appid`，请替换成对应的参数，`{extParam}` 是分享参数，具体见分享章节介绍

**存量投放链接处理**

若之前投放的链接是 `pages/player/index`，可使用 `redirectTo` 跳转到播放器的页面链接
（即 `plugin-private://wx94a6522b1d640c3b/pages/playlet/playlet` 开头的链接），用户在播放器页面点击左上角后，可返回首页。

## 七、版本更新日志

[查看更新日志](videoplayer-changelog)

## 八、附录

### 8.1 完整接口文档

[查看完整接口文档](videoplayer-interface)

### 8.2 播放器后台返回码

|值 |描述 |
|--|--|
|-1 |系统错误，请重试 |
|0 |成功 |
|1 |不存在 |
|2 |参数错误 |
|21000 |该剧未授权播放，请通过后台api进行授权 |
|21001 |用户加密的sessionkey不存在 |
|21002 |商家的加密包未进行base64编码 |
|21003 |解密商家加密包失败，请确认是否按接入文档中给出的加密方案加密或sessionkey要使用当前用户最新的 |
|21004 |商家加密前的明文不是合法的json格式或者字段类型不对，请按接入文档排查 |
|21005 |商家加密中的必填参数值(openid、src_appid及drama_id)为空 |
|21006 |播放器的入参与商家加密包中的参数值不匹配 |
|21007 |加密包中的openid无效 |	
|21008 |21008 开发者未开启预拉取 |
|21009 |21009 商家流量不足 |

### 8.3 接入咨询

短剧贴片申请：[问卷填写](https://wj.qq.com/s2/14472761/fk8o/)
