更新记录

1.1.9(2026-02-03)

修复版本管理问题,新增文档

1.1.8.1(2026-02-02)

文档更新

1.1.8(2026-02-02)

修复时序考虑不周导致的非加密文件被误判定为加密的问题

查看更多

平台兼容性

uni-app(4.55)

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

uni-app x(4.55)

Chrome Safari Android Android插件版本 iOS 鸿蒙 微信小程序
- - 5.0 1.1.7 - - -

使用前须知

本插件在uni-app xuni-app原生项目上测试通过,请注意支持导入的HBuilder版本。如存在使用插件出错情况,欢迎联系我反馈。

插件引入

import { M3u82Mp4, CurrentProgress, clearCache,onCurrentProgressChange,MulM3u82Mp4 } from '@/uni_modules/cfit-m3u8'

插件使用

远程m3u8合成

M3u82Mp4('https://cfitsec.cn/monologue/Monologue.m3u8',(filePath) => {
    console.log('下载完成:', filePath)
    uni.showToast({ title: '保存至: ' + filePath, icon: 'none' })
})
//获取一次ts文件下载进度
CurrentProgress((progress) => {
    console.log("Progress:", progress)
})
//下载进度变化时获取其进度
onCurrentProgressChange((progress) => {
    console.log("Progress:", progress)
})

下载成功会返回形如/storage/emulated/0/Android/data/io.dcloud.uniappx/files/Download/video_1742051459918.mp4的文件路径,失败则统一返回null,可手动判断成功与否。

本地m3u8合成

也可以传入本地m3u8文件,使用file://开头即可。 示例:

M3u82Mp4('file:///storage/emulated/0/Download/monologue/monologue.m3u8',(filePath) => {
    console.log('下载完成:', filePath)
    uni.showToast({ title: '保存至: ' + filePath, icon: 'none' })
})
//获取一次ts文件下载进度
CurrentProgress((progress) => {
    console.log("Progress:", progress)
})
//下载进度变化时获取其进度
onCurrentProgressChange((progress) => {
    console.log("Progress:", progress)
})

其中/storage/emulated/0/Download/monologue/monologue.m3u8为实际本地路径名。

多任务m3u8合成

var urls = ['file:///storage/emulated/0/Download/monologue/monologue.m3u8','https://cfitsec.cn/monologue/Monologue.m3u8'];//链接地址
var paths = ['/sdcard/DCIM/','/sdcard/DCIM/0004.mp4'];//对应保存路径,应当有写入权限
var options = {
    retry: true, //是否错误重试
    time: 3 //错误重试次数,仅retry开启true后有效
};//目前支持此两个参数
var index = 0;
MulM3u82Mp4(urls, paths, options, (path) => {
    //每次返回的path分别是对应urls内链接/文件的地址
    console.log(`第${index}个的地址是${path}`);
    index+=1;
});

清除下载缓存

示例:

clearCache(); //引入后可以直接调用

下载中不推荐启动本函数,可能导致下载出错(示例中下载时禁用了缓存清除按钮)

注意事项

  • 运行本插件进行本地m3u8的合并时需要确保读写权限开启;
  • 如果运行时报错open failed: EACCES (Permission denied),意味着读取外部路径文件权限未开启。
  • 在本地合成带AES128加密的ts文件时出现如上错误,一般是key被拒绝读取,如果不想开权限,则可以修改key的拓展名例如ts等等(我要不知道为啥,但是这样可以过),以及修改m3u8的指向key路径。
  • uni-app x在本地合成UC浏览器等的m3u8文件如果报错,一般是因为读取ts文件没有拓展名导致的拒绝访问。此时可以手动修改ts文件拓展名,或者开启更多文件权限,或者改成在uniapp项目运行。
  • 保存到相册如果结尾带拓展名.mp4.ts,是uni.saveVideoToPhotosAlbum保存时添加的,本插件保存的拓展名为正常的mp4。实际生产环境可以换成其他保存文件的代码。

    示例代码

    可部署插件后在index.vue测试如下代码:

    <!--index.vue-->
    <template>
    <view class="content">
        <input class="input" v-model="url" placeholder="Enter M3u8 URL. . ." confirm-type="send" />
        <button class="action-btn" @click="display">展示/隐藏视频</button>
        <view v-if="isshow" class="video-container">
            <video class="video-player" :src="url"></video>
            <text class="url-text">{{url}}</text>
        </view>
        <button class="action-btn" @click="clear" :disabled="start">清除下载缓存</button>
        <button class="action-btn" @click="download" :disabled="start">下载文件</button>
        <text class="url-text" v-if="start">ts下载进度:{{ progress }} %</text>
        <text class="url-text" v-if="path!=''">下载的mp4路径:{{ path }}</text>
        <button class="action-btn" v-if="path!=''" @click="open">保存到相册</button>
    </view>
    </template>
    <script>
    // 引入插件
    import { M3u82Mp4, CurrentProgress, clearCache } from '@/uni_modules/cfit-m3u8'
    export default {
        data() {
            return {
                url: 'https://cfitsec.cn/monologue/Monologue.m3u8',
                isshow: false,
                path: "",
                interv: 0,
                start: false,
                progress: 0
            }
        },
        onLoad() {
        },
        methods: {
            display() {
                this.isshow = !this.isshow;
            },
            download() {
                this.progress = 0;
                var that = this;
                uni.showLoading({
                    title: '下载中. . .'
                })
                this.start = true;
                M3u82Mp4(
                    that.url,
                    (filePath) => {
                        this.start = false;
                        uni.hideLoading();
                        try {
                            clearInterval(that.interv)
                        } catch (_) {
                        }
                        console.log('下载完成:', filePath)
                        uni.showToast({ title: '保存至: ' + filePath, icon: 'none' })
                        that.path = filePath;
                    }
                )
                this.interv = setInterval(function () {
                    CurrentProgress((progress) => {
                        console.log("Progress:", progress)
                        that.progress = progress;
                        if (progress == 100) {
                            try {
                                clearInterval(that.interv)
                            } catch (_) {
                            }
                        }
                    })
                }, 50)
            }, clear(){
                clearCache();
                uni.showToast({
                    position: "center",
                    icon: "none",
                    title: "缓存已清除"
                });
            },open() {
                uni.saveVideoToPhotosAlbum({
                    filePath: this.path,//
                    success: (e) => {
                        uni.showToast({
                            position: "center",
                            icon: "none",
                            title: "视频保存成功,请到手机相册查看"
                        });
                    },
                    fail: (_) => {
                        uni.showToast({
                            position: "center",
                            icon: "none",
                            title: "保存失败"
                        });
                    }
                });
            }
        }
    }
    </script>
    <style>
    .content {
        display: flex;
        flex-direction: column;
        align-items: center;
        padding: 40rpx 30rpx;
        background-color: #f8f8f8;
    }
    .input {
        padding: 20px;
        margin: 20px;
        height: 60px;
        border: 1px solid #e5e5e5;
        border-radius: 35rpx;
        background: #fff;
    }
    .action-btn {
        width: 80%;
        height: 80rpx;
        line-height: 80rpx;
        border-radius: 40rpx;
        background: #007AFF;
        color: white;
        font-size: 32rpx;
        margin-bottom: 30rpx;
        box-shadow: 0 4rpx 12rpx rgba(0, 122, 255, 0.3);
    }
    .download-btn {
        background: #34C759;
        box-shadow: 0 4rpx 12rpx rgba(52, 199, 89, 0.3);
    }
    .video-container {
        width: 100%;
        margin: 40rpx 0;
        background: white;
        border-radius: 16rpx;
        padding: 20rpx;
        box-shadow: 0 4rpx 24rpx rgba(0, 0, 0, 0.08);
    }
    .video-player {
        width: 100%;
        height: 400rpx;
        border-radius: 12rpx;
        overflow: hidden;
        background: black;
    }
    .url-text {
        margin-top: 30rpx;
        padding: 20rpx;
        background: #f4f4f4;
        border-radius: 8rpx;
        font-size: 26rpx;
        line-height: 1.6;
        color: #0066ff;
    }
    </style>

隐私、权限声明

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

android.permission.WRITE_EXTERNAL_STORAGE android.permission.INTERNET android.permission.ACCESS_NETWORK_STATE

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

插件不采集任何数据

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