AI视频功能开发——IPC 精彩时刻通用模版开发流程(二)

8. 精彩时刻全量相册

功能介绍

精彩时刻全量相册主要包含以下内容:

  • 1.精彩时刻全量视频片段展示;
  • 2.支持视频下载、AI 编辑、删除;

获取精彩时刻全量相册数据

相关代码段

import {
  Button,
  View,
  showToast,
  showModal,
  albumFileDelete,
  albumVideoFileList,
} from "@ray-js/ray";
import { useBatchDecryptImage } from "@/features";
import { deleteImages } from "@ray-js/ray-ipc-decrypt-image";

const { timeAlbumId, stockId, secret, onShow, homeId } = props;
const batchLoadInstance = useBatchDecryptImage(true, 400);

const getPageData = (params) => {
  return new Promise((resolve, reject) => {
    if (isNaN(Number(timeAlbumId)) || isNaN(Number(stockId))) {
      showToast({ title: I18n.t("request.param.error") });
      return;
    }
    const param: IAlbumVideoFileListReq = {
      stockId: Number(stockId), // 库存id
      gid: homeId, // 家庭id
      timeAlbumId: Number(timeAlbumId), // 相册id
      pageNum: params.page,
      // 为了避免
      endTime: endTimeRef.current,
      // endTime: 1740386449,
      pageSize: params.pageSize,
    };
    albumVideoFileList(param).then((result: IAlbumVideoFileListRes) => {
      if (result && result.list && result.list.length > 0) {
        const res = result.list.map((item) => {
          const fileUrl = item.coverInfo?.fileUrl || "";
          const fileName = fileUrl.split("?")[0].split("/").pop();
          batchLoadInstance.addOriginData({
            fileUrl,
            decryptKey: secret,
          });
          return {
            ...item,
            coverInfo: { ...item.coverInfo, ...{ fileName } },
          };
        });
        batchLoadInstance.loadAllOriginData();
        resolve({ data: res, total: result.total });
        return;
      }
      resolve({ data: [] });
    });
  });
};

下载、合并、删除精彩时刻全量相册视频

相关代码段

import {
  Button,
  View,
  showToast,
  showModal,
  albumFileDelete,
  albumVideoFileList,
} from '@ray-js/ray'

import {
  cancelVideoDownload,
  composeVideos,
  getAuthorize,
  imageDownload,
  videoDownload,
} from '@/features/downloadImage'

import { jumpToAiVideoEdit, jumpToPlayer } from '@/features/jump-to-page'

  const [selectedIds, setSelectedIds] = useImmer([])
  const [selectIdIndexMap, setSelectIdIndexMap] = useState<Map<number, number>>(new Map())

  const getSelectDatas = () => {
    const maxCount = selectedIds.length
    let currentCount = 0
    const result = []
    for (let i = 0; i < cloudData.length; i++) {
      if (currentCount >= maxCount) break
      const item = cloudData[i]
      const index = selectedIds.indexOf(item.id)
      if (index >= 0) {
        result[index] = item
        currentCount++
      }
    }
    return result
  }

 const onOperateCompose = async (e, type) => {
    if (type === EOperate.download) {
      isCloseDownload.current = false
      if (selectedIds.length > MAX_DOWNLOAD_SIZE) {
        showToast({
          title: I18n.format('download.count.limit.desc', { time: MAX_DOWNLOAD_SIZE }),
          icon: 'error',
        })
        return
      }
      const selectDatas = getSelectDatas()
      const bl = await getAuthorize()
      if (bl) {
        onShow({ show: true, title: I18n.t('downloading.video'), type: ELoadingType.loading })
        const indexErrs = []
        // 下载一个成功后,再去下载另一个
        for (let i = 0; i < selectDatas.length; i++) {
          if (isCloseDownload.current) {
            break
          }
          const ele = selectDatas[i]
          if (ele.type === 1 || ele.type === 2) {
            const dl = await videoDownload(deviceId, ele.mediaInfo?.fileUrl, secret)
            if (dl === -1) {
              indexErrs.push(i)
            }
          } else if (ele.type === 0) {
            // 视频不存在,就下载图片
            const dl = await imageDownload(deviceId, ele.coverInfo?.fileUrl, secret)
            if (dl === -1) {
              indexErrs.push(i)
            }
          } else {
            indexErrs.push(i) // 说明云端返回类型不对
          }
        }
        onShow({ show: false, title: I18n.t('download.success'), type: ELoadingType.success })
        if (indexErrs.length > 0) {
          const str = indexErrs.join(',')
          showModal({
            title: '',
            content: str + I18n.t('download.clip.error'),
            showCancel: false,
            confirmText: I18n.t('common.confirm'),
          })
        } else {
          setSelectedIds([])
          setSelectIdIndexMap(null)
        }
      }
    } else if (type === EOperate.compose) {
      if (selectedIds.length > MAX_COMPOSE_SIZE) {
        showToast({
          title: I18n.format('compose.count.limit.desc', { time: MAX_COMPOSE_SIZE }),
          icon: 'error',
        })
        return
      }
      const selectDatas = getSelectDatas()
      const isVideos = isExistSingleImage(selectDatas)
      if (isVideos) {
        showToast({ title: I18n.t('compose.only.media'), icon: 'error' })
        return
      }
      const bl = await getAuthorize()
      if (bl) {
        const mediaInfos = selectDatas.map((item) => {
          return {
            fileUrl: item.mediaInfo?.fileUrl || '',
            key: secret,
          }
        })
        const json = JSON.stringify({ fileInfo: mediaInfos })
        onShow({ show: true, title: I18n.t('composing.video'), type: ELoadingType.loading })
        const result = await composeVideos(deviceId, json)
        if (result === -1) {
          onShow({ show: false, title: I18n.t('compose.fail'), type: ELoadingType.error })
        } else {
          onShow({ show: false, title: I18n.t('compose.success'), type: ELoadingType.success })
          setSelectedIds([])
          setSelectIdIndexMap(null)
          const { path = '', thingfilePath = '' } = result as Record<string, any>
          setTimeout(() => {
            jumpToAiVideoEdit({ videoPath: thingfilePath, videoOriginPath: path })
          }, 0)
        }
      }
    } else if (type === EOperate.delete) {
      if (isNaN(Number(stockId))) {
        showToast({ title: I18n.t('request.param.error') })
        return
      }
      if (selectedIds.length > MAX_DELETE_SIZE) {
        showToast({
          title: I18n.format('delete.count.limit.desc', { time: MAX_DELETE_SIZE }),
          icon: 'error',
        })
        return
      }

      showModal({
        title: '',
        content: I18n.t('module.delete.desc'),
        cancelText: I18n.t('common.cancel'),
        confirmText: I18n.t('aiVisual.delete'),
        success: async ({ confirm, cancel }) => {
          console.log('res===1', confirm, cancel)
          if (confirm) {
            const param: IAlbumFileDeleteReq = {
              stockId: Number(stockId),
              gid: homeId,
              timeAlbumId: Number(timeAlbumId),
              albumRecordIds: selectedIds.join(','),
            }
            const result = await albumFileDelete(param)
            if (result) {
              showToast({ title: I18n.t('aiVisual.delete.success'), icon: 'success' })
              const curCloudDatas = getCurCloudDatas()
              const selectDatas = curCloudDatas.filter((item) => selectedIds.includes(item.id))
              const fileNames = selectDatas.map((item) => item.coverInfo.fileName)
              deleteImages(deviceId, fileNames)
              setDeletedIds([...new Set([...deletedIds, ...selectedIds])])
              setSelectedIds([])
              setSelectIdIndexMap(null)
            } else {
              showToast({ title: I18n.t('aiVisual.delete.fail'), icon: 'error' })
            }
          }
        },
      })
    }
  }

9. AI 二次编辑

功能介绍

精彩时刻视频 AI 编辑主要包含以下内容:

  • 视频主体突出 AI 编辑;
  • 视频隐私保护 AI 编辑;

视频主体突出 AI 编辑相关开发信息,可参考 AI 视频流主体突出功能模版

视频隐私保护 AI 编辑

相关代码段

import { ai } from "@ray-js/ray";
import { createTempVideoRoot } from "@/utils";

const {
  objectDetectCreate,
  objectDetectDestroy,
  objectDetectForVideo,
  objectDetectForVideoCancel,
  offVideoObjectDetectProgress,
  onVideoObjectDetectProgress,
} = ai;

// 当前 AI 视频的编辑状态
const [handleState, setHandleState] = useState("idle");
// 当前 AI 视频流处理进度
const [progressState, setProgressState] = useState(0);

// 注册 App AI 实例
useEffect(() => {
  objectDetectCreate();

  return () => {
    // 页面销毁时同时销毁 App AI 实例
    objectDetectDestroy();
    audioManager.destroy({
      success: (res) => {
        console.log("==destroy2==success", res);
      },
      fail: (error) => {
        console.log("==destroy2==fail", error);
      },
    });
  };
}, []);

// AI 视频流处理功能
const handleVideoByAI = (
  detectType: number,
  imageEditType: number,
  musicPath = ""
) => {
  const tempVideoPath = createTempVideoRoot();
  onVideoObjectDetectProgress(handleListenerProgress);
  privacyProtectDetectForVideo({
    inputVideoPath: videoOriginSrc,
    outputVideoPath: tempVideoPath,
    detectType,
    musicPath,
    originAudioVolume: volumeObj.video / 100,

    overlayAudioVolume: volumeObj.music / 100,
    imageEditType,
    audioEditType: 3,
    success: ({ path }) => {
      console.log("===path", path, tempVideoPath);
      offVideoObjectDetectProgress(handleListenerProgress);
      setProgressState(0);
      fetchVideoThumbnails({
        filePath: path,
        startTime: 0,
        endTime: 10,
        thumbnailCount: 1,
        thumbnailWidth: 375,
        thumbnailHeight: 212,
        success: (res) => {
          console.log("===fetchVideoThumbnails==res", res);
          setHandleState("success");
          setVideoSrc(path);
          setVideoOriginSrc(path);
          setPosterSrc(res?.thumbnailsPath[0]);
          showToast({
            title: I18n.t("dsc_ai_generates_success"),
            icon: "success",
          });
        },
        fail: ({ errorMsg }) => {
          console.log("==fetchVideoThumbnails==fail==", errorMsg);
        },
      });
    },
    fail: ({ errorMsg }) => {
      console.log("==objectDetectForVideo==fail==", errorMsg);
      offVideoObjectDetectProgress(handleListenerProgress);
      setProgressState(0);
      setHandleState("fail");
      setHandleState("selectSkill");
      setIsShowAISkills(true);
      showToast({
        title: I18n.t("dsc_ai_generates_fail"),
        icon: "error",
      });
    },
  });
};

// 打断 AI 隐私保护视频流生成
const handleCancelAIProcess = () => {
  objectDetectForVideoCancel({
    success: () => {
      setHandleState("select");
    },
  });
};

具体 AI 技术方案介绍,详见:视频解决方案—IPC 精彩时刻通用方案

具体 API 相关介绍,详见:开发者文档—AI 基础包

10. 结束

  • 恭喜你 🎉 完成了本教程的学习!
  • 有任何问题可以提交工单咨询。 
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IoT砖家涂拉拉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值