更新记录

1.0.0(2025-10-16)

  • 首发版本

平台兼容性

uni-app(4.71)

Vue2 Vue2插件版本 Vue3 Vue2插件版本 Chrome Safari app-vue app-nvue Android iOS iOS插件版本 鸿蒙
1.0.0 1.0.0 - - - - - 16 1.0.0 -
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 快应用-华为 快应用-联盟
- - - - - - - - - - -

uni-app x(4.71)

Chrome Safari Android iOS 鸿蒙 微信小程序
- - - - - -

UTS环境兼容性

uni-app uni-app x

sn-parse-livephoto

插件简介

sn-parse-livephoto 是一个用于将视频转换为 iOS Live Photo 格式的 UTS 插件。该插件支持从视频文件或相册中选择的视频创建 Live Photo,并自动保存到系统相册中。

注意:本插件仅支持 iOS 平台

功能特性

  • 🎥 从相册选择视频并转换为 Live Photo
  • 📁 从本地视频文件创建 Live Photo
  • ⏰ 支持自定义封面时间点
  • ✂️ 支持视频片段裁剪
  • 📱 自动保存到系统相册
  • 🔧 基于 UTS 开发,性能优异

安装配置

1. 插件安装

在项目中引入插件:

import { pick2Livephoto, parseLivephoto } from '@/uni_modules/sn-parse-livephoto'

2. 权限配置

manifest.json 中添加相册权限:

{
  "ios": {
    "privacyDescription": {
      "NSPhotoLibraryUsageDescription": "需要访问相册以选择视频并创建 Live Photo",
      "NSPhotoLibraryAddUsageDescription": "需要访问相册以保存 Live Photo"
    }
  }
}

API 接口

pick2Livephoto

从相册选择视频并转换为 Live Photo。

参数:

interface SnParsePickerLivephotoParams {
  coverTime?: number;    // 可选,封面时间点(秒),默认为视频中间位置
  videoRange?: number[]; // 可选,视频片段范围 [开始时间, 结束时间](秒)
}

回调参数:

interface SnParseLivephotoResult {
  code: number;          // 0: 成功, -1: 失败
  data: UTSJSONObject;   // 结果数据
}

使用示例:

import { pick2Livephoto } from '@/uni_modules/sn-parse-livephoto'

// 选择视频并创建 Live Photo
pick2Livephoto({
  coverTime: 2.0,        // 封面时间设为2秒
  videoRange: [1.0, 5.0] // 只使用1-5秒的视频片段
}, (result) => {
  if (result.code === 0) {
    console.log('Live Photo 创建成功')
    uni.showToast({
      icon: 'success',
      title: 'Live Photo 创建成功'
    })
  } else {
    console.error('创建失败:', result.data.errMsg)
    uni.showToast({
      icon: 'error',
      title: result.data.errMsg || '创建失败'
    })
  }
})

parseLivephoto

从本地视频文件创建 Live Photo。

参数:

interface SnParseLivephotoParams {
  video: string;         // 必需,视频文件路径
  coverTime?: number;    // 可选,封面时间点(秒)
  videoRange?: number[]; // 可选,视频片段范围 [开始时间, 结束时间](秒)
}

回调参数:

interface SnParseLivephotoResult {
  code: number;          // 0: 成功, -1: 失败
  data: UTSJSONObject;   // 结果数据
}

使用示例:

import { parseLivephoto } from '@/uni_modules/sn-parse-livephoto'

// 从本地视频文件创建 Live Photo
const videoPath = plus.io.convertLocalFileSystemURL('/static/test.mov')
parseLivephoto({
  video: videoPath,
  coverTime: 1.5,        // 封面时间设为1.5秒
  videoRange: [0.5, 3.0] // 只使用0.5-3秒的视频片段
}, (result) => {
  if (result.code === 0) {
    console.log('Live Photo 创建成功')
    uni.showToast({
      icon: 'success',
      title: 'Live Photo 创建成功'
    })
  } else {
    console.error('创建失败:', result.data.errMsg)
    uni.showToast({
      icon: 'error',
      title: result.data.errMsg || '创建失败'
    })
  }
})

完整使用示例

<template>
  <view class="container">
    <view class="section">
      <text class="title">Live Photo 创建工具</text>

      <button type="primary" @click="pickVideoFromAlbum" class="btn">
        从相册选择视频
      </button>

      <button type="default" @click="createFromLocalVideo" class="btn">
        从本地视频创建
      </button>

      <view class="settings" v-if="showSettings">
        <text class="label">封面时间点(秒):</text>
        <input v-model="coverTime" type="number" placeholder="例如:2.0" />

        <text class="label">视频片段范围:</text>
        <view class="range-input">
          <input v-model="videoStart" type="number" placeholder="开始时间" />
          <text>-</text>
          <input v-model="videoEnd" type="number" placeholder="结束时间" />
        </view>
      </view>

      <button type="default" @click="toggleSettings" class="btn-small">
        {{ showSettings ? '隐藏' : '显示' }}高级设置
      </button>
    </view>
  </view>
</template>

<script>
import { pick2Livephoto, parseLivephoto } from '@/uni_modules/sn-parse-livephoto'

export default {
  data() {
    return {
      showSettings: false,
      coverTime: 2.0,
      videoStart: 1.0,
      videoEnd: 5.0
    }
  },
  methods: {
    // 从相册选择视频
    pickVideoFromAlbum() {
      const params = {
        coverTime: parseFloat(this.coverTime) || undefined
      }

      // 如果设置了视频范围
      if (this.videoStart && this.videoEnd) {
        params.videoRange = [
          parseFloat(this.videoStart),
          parseFloat(this.videoEnd)
        ]
      }

      pick2Livephoto(params, (result) => {
        this.handleResult(result)
      })
    },

    // 从本地视频创建
    createFromLocalVideo() {
      // 选择本地视频文件
      uni.chooseMedia({
        count: 1,
        mediaType: ['video'],
        sourceType: ['album'],
        success: (res) => {
          const videoPath = plus.io.convertLocalFileSystemURL(res.tempFiles[0].tempFilePath)

          const params = {
            video: videoPath,
            coverTime: parseFloat(this.coverTime) || undefined
          }

          // 如果设置了视频范围
          if (this.videoStart && this.videoEnd) {
            params.videoRange = [
              parseFloat(this.videoStart),
              parseFloat(this.videoEnd)
            ]
          }

          parseLivephoto(params, (result) => {
            this.handleResult(result)
          })
        },
        fail: (err) => {
          console.error('选择视频失败:', err)
          uni.showToast({
            icon: 'error',
            title: '选择视频失败'
          })
        }
      })
    },

    // 处理结果
    handleResult(result) {
      if (result.code === 0) {
        uni.showToast({
          icon: 'success',
          title: 'Live Photo 创建成功'
        })
        console.log('创建成功:', result.data.message)
      } else {
        uni.showToast({
          icon: 'error',
          title: result.data.errMsg || '创建失败'
        })
        console.error('创建失败:', result.data.errMsg)
      }
    },

    // 切换设置显示
    toggleSettings() {
      this.showSettings = !this.showSettings
    }
  }
}
</script>

<style>
.container {
  padding: 20px;
}

.section {
  margin-bottom: 30px;
}

.title {
  font-size: 18px;
  font-weight: bold;
  margin-bottom: 20px;
  display: block;
}

.btn {
  width: 100%;
  margin-bottom: 10px;
}

.btn-small {
  width: 100%;
  font-size: 14px;
}

.settings {
  margin: 20px 0;
  padding: 15px;
  background: #f5f5f5;
  border-radius: 8px;
}

.label {
  display: block;
  margin: 10px 0 5px 0;
  font-size: 14px;
  color: #666;
}

input {
  width: 100%;
  padding: 8px;
  border: 1px solid #ddd;
  border-radius: 4px;
  margin-bottom: 10px;
}

.range-input {
  display: flex;
  align-items: center;
  gap: 10px;
}

.range-input input {
  flex: 1;
  margin-bottom: 0;
}
</style>

注意事项

平台限制

  • 仅支持 iOS 平台,Android 平台不支持
  • 需要 iOS 12.0 及以上版本
  • 需要支持 Live Photo 的设备

权限要求

  • 需要相册访问权限(NSPhotoLibraryUsageDescription)
  • 需要相册写入权限(NSPhotoLibraryAddUsageDescription)
  • 首次使用时会自动请求权限

文件要求

  • 支持的视频格式:MP4、MOV、M4V 等
  • 视频时长建议在 1-15 秒之间
  • 视频文件大小建议不超过 100MB

性能建议

  • 视频片段长度建议控制在 5 秒以内
  • 避免同时处理多个视频文件
  • 大文件处理时建议显示加载提示

错误处理

  • 请务必处理回调中的错误情况
  • 常见错误包括权限拒绝、文件不存在、格式不支持等
  • 建议在用户操作前检查权限状态

错误码说明

  • code: 0 - 操作成功
  • code: -1 - 操作失败

常见错误信息:

  • "复制视频失败" - 文件复制过程中出错
  • "修复错误" - 视频文件处理失败
  • "用户取消选择" - 用户在相册选择界面取消了操作
  • "无法获取视频资源" - 选择的不是有效的视频文件
  • "视频文件不存在" - 传入的文件路径对应的文件不存在
  • "创建 Live Photo 失败" - 无法将视频转换为 Live Photo

隐私、权限声明

1. 本插件需要申请的系统权限列表:

- 需要相册访问权限(NSPhotoLibraryUsageDescription) - 需要相册写入权限(NSPhotoLibraryAddUsageDescription)

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

暂无用户评论。