一键下载 M3U8 并转换为 MP4升级版

news2025/2/28 12:01:08

 之前的下载 M3U8程序,有很多问题, 为此做了一些升级,分享给大家。

  • 增加了存在播放列表的情况处理
  • 播放列表路径和ts路径错误问题
  • 多线程问题
  • 对于电视剧多文件下载的处理

这里从网上找了一部的链接,可以参考这个网站https://www.zuida001.com/


import os
import urllib3
import requests
import subprocess
import m3u8
from urllib import parse
from tenacity import retry, wait_random, stop_after_attempt
import gevent
from gevent.threadpool import ThreadPool

urllib3.disable_warnings()

class M3u8Downloader:
    def __init__(self, pool_size=10):
        self.pool = ThreadPool(pool_size)


    @retry(stop=stop_after_attempt(3), wait=wait_random(2, 5))
    def request(self, url):
        """发送请求"""
        try:
            res = requests.get(url, verify=False, timeout=5)
            return res
        except Exception as e:
            print(url, e)
            raise e


    def download_segment(self, url, file):
        """下载ts文件"""
        if os.path.exists(file):
            return 

        res = self.request(url)
        with open(file, "wb")as f:
            f.write(res.content)


    def convert_mp4(self, path, output, key):
        if not os.path.exists(output):
            # 使用FFmpeg将所有.ts文件合并为一个MP4文件    ffmpeg -allowed_extensions ALL -i index.m3u8 -c copy xxx.mp4
            if key:
                subprocess.call(['ffmpeg', '-allowed_extensions', 'ALL', '-i', 'local.m3u8', '-c', 'copy', output], cwd=path)
            else:
                subprocess.call(['ffmpeg', '-i', 'local.m3u8', '-c', 'copy', output], cwd=path)

    def download_m3u8(self, url, path):
        """下载M3U8文件,有些存在播放列表,默认选择第一个"""
        m3u8_file_name = os.path.join(path, "index.m3u8")
        res = self.request(url)
        with open(m3u8_file_name, "w", encoding="utf-8")as f:
            f.write(res.text)

        # 解析M3U8文件
        m3u8_obj = m3u8.loads(res.text)

        # 如果存在清晰度列表,请求解析清晰度列表
        m3u8_playlist = []
        for playlist in m3u8_obj.playlists:
            uri = parse.urljoin(url, playlist.uri)
            bandwidth = playlist.stream_info.bandwidth
            resolution= playlist.stream_info.resolution

            m3u8_file_name = os.path.join(path, f"{'x'.join([str(i) for i in resolution])}_{bandwidth}.m3u8")
            res = self.request(uri)
            with open(m3u8_file_name, "w", encoding="utf-8")as f:
                f.write(res.text)
            cur_m3u8 = m3u8.loads(res.text)
            cur_m3u8.uri = uri
            m3u8_playlist.append(cur_m3u8)

        if m3u8_playlist:
            # 播放列表默认选择第一个
            return m3u8_playlist[0]
        else:
            return m3u8_obj


    def download(self, url, path, output):
        """
        下载单个m3u8主程序
        url: m3u8链接url
        path: 单个m3u8目录
        output: 转换输出文件路径名
        """
        # 创建目录
        segment_dir = os.path.join(path, "index")
        if not os.path.exists(segment_dir):
            os.makedirs(segment_dir)
        output_dir = os.path.dirname(output)
        if not os.path.exists(output_dir):
            os.makedirs(output_dir)

        # 解析M3U8文件
        m3u8_obj = self.download_m3u8(url, path)

        # 下载key文件 
        for item in m3u8_obj.keys:
            if item:
                key_url = parse.urljoin(url, item.uri)
                key_file_name = os.path.join(path, key_url.split("/")[-1])
                res = self.request(key_url)
                with open(key_file_name, "w", encoding="utf-8")as f:
                    f.write(res.text)

        segments = []
        # 生成新的本地M3U8文件内容
        for index, segment in enumerate(m3u8_obj.segments):
            uri = parse.urljoin(m3u8_obj.uri, segment.uri)
            segments.append(uri)
            # 有些ts文件名过长,对其以序号重新命名
            segment.uri = f"index/{index}.{uri.split('.')[-1]}"

        # 保存M3U8文件
        local_file_name = os.path.join(path, "local.m3u8")
        with open(local_file_name, 'w') as f:
            f.write(m3u8_obj.dumps())

        # 下载ts文件
        for index, url in enumerate(segments):
            file = os.path.join(segment_dir, f"{index}.{url.split('.')[-1]}")
            self.pool.spawn(self.download_segment, url, file)
        gevent.wait()

        # ts文件下载完成, 转换成mp4文件
        if len(segments) == len(os.listdir(segment_dir)):
            self.convert_mp4(path, output, key=[item.uri for item in m3u8_obj.keys if item])


if __name__ == "__main__":
    cur_path = os.path.abspath(os.path.dirname(__file__))
    data = [
        {"name": "凶劫601航班第01集", "url": "https://v4.mstopq.com/202404/11/4cSTt8dMgB7/video/index.m3u8"},
        {"name": "凶劫601航班第02集", "url": "https://v4.mstopq.com/202404/11/JtNttP8HfS7/video/index.m3u8"},
        {"name": "凶劫601航班第03集", "url": "https://v4.mstopq.com/202404/11/b10TUD8C4T7/video/index.m3u8"},
        {"name": "凶劫601航班第04集", "url": "https://v4.mstopq.com/202404/11/eWLuKWqFBL7/video/index.m3u8"},
        {"name": "凶劫601航班第05集", "url": "https://v4.mstopq.com/202404/11/Sj3pwtX0hN7/video/index.m3u8"},
        {"name": "凶劫601航班第06集", "url": "https://v4.mstopq.com/202404/11/i62BN8wMfc7/video/index.m3u8"},
    ]

    for item in data:
        url = item["url"]
        name = item["name"]
        path = os.path.join(cur_path, "凶劫601航班_tmp", f"{name}") 
        output = os.path.join(cur_path, "凶劫601航班", f"{name}.mp4")

        # 因为是多线程下载,可能存在某个线程下载失败的情况, 如果发现下载不完整,没有输出文件,可以尝试重新运行,已经下载过的不会再次下载。
        download = M3u8Downloader(pool_size=20)
        download.download(url, path, output)

查看原文:一键下载 M3U8 并转换为 MP4升级版

 关注公众号 "字节航海家" 及时获取最新内容

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1585079.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

YOLOV5训练KITTI数据集实践

目录 一、YOLOV5下载安装二、KITTI数据集三、标签格式转换四、修改配置文件五、训练六、测试 一、YOLOV5下载安装 git clone https://github.com/ultralytics/yolov5.git conda create -n yolov5 python3.8 -y conda activate yolov5 cd yolov5 pip install -r requirements.t…

百度OCR身份证识别C++离线SDKV3.0 C#对接

百度OCR身份证识别C离线SDKV3.0 C#对接 目录 说明 效果 问题 项目 代码 下载 说明 自己根据SDK封装了动态库,然后C#调用。 SDK 简介 本 SDK 适应于于 Windows 平台下的⾝份证识别系统,⽀持 C接⼜开发的 SDK,开发者可在VS2015 下⾯进⾏开发(推荐…

基于FPGA的HDMI设计导航页面

FPGA使用HDMI更多时候用于传输图像数据,并不会传输音频数据,因此以下文章均采用DVI接口协议,HDMI与DVI的视频传输协议基本一致,区别也很小。 首先需要了解HDMI的来源,以及物理接口类型以及引脚信号,最后对几…

网站SEO关键词规划时如何筛选出合适的关键词?

在网站SEO优化过程中,关键词布局是一个至关重要的环节。首先,我们需要确定核心关键词,然后通过各种策略和方法对关键词进行扩展。完成关键词扩展后,接下来的任务就是对这些扩展后的关键词进行筛选。那么,如何进行有效的…

day02 51单片机

51单片机学习 1闪烁LED 1.1 需求描述 这个案例,我们要让P00引脚对应的LED按照1秒闪烁1次。 1.2 硬件设计 1.1 软件设计 1)LED闪烁的代码 想让LED闪烁,就需要P00的值不断在0和1之间循环变化。实现这一功能的代码也很简单: #include <STC89C5xRC.H> //包含STC89…

在线预约小程序怎么做

在快节奏的现代生活中&#xff0c;无论是预约理发、还是预定餐厅&#xff0c;亦或是挂号就医&#xff0c;我们都希望有一个更加便捷、高效的方式来完成这些任务。而今&#xff0c;随着科技的发展&#xff0c;一款全新的在线预约小程序应运而生&#xff0c;为我们的生活带来了前…

SOCKS代理是如何增强网络隐私?

在数字化时代&#x1f310;&#xff0c;网络隐私的重要性日益凸显。个人和组织都在寻找有效的方法来保护自己的网络活动不受侵犯。SOCKS代理作为一种流行的网络协议&#xff0c;提供了一种有效的手段来增强网络隐私。本文将详细介绍SOCKS代理是如何工作的&#xff0c;以及它是如…

【随笔】Git 高级篇 -- 本地栈式提交 rebase | cherry-pick(十七)

&#x1f48c; 所属专栏&#xff1a;【Git】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大…

不允许在constexpr函数中进行声明

这是我用pycharm在windows系统下复现sfm深度学习网络(Deep Two-View Structure-from-Motion Revisited&#xff09;遇见的问题&#xff0c;复现时有段代码pytorch扩展cuda/c&#xff0c;pycharm中出现C标准相关的报错如下&#xff1a; 在网上查找很久无果&#xff0c;后面通过…

局域网tcp通信实验

两台windows系统计算机简单TCP通信测试_两台计算机tcp通信-CSDN博客 使用这篇文章的小工具。 环境&#xff1a; 我和同学的两台笔记本电脑。 使用我的手机开热点&#xff0c;两台电脑连接热点。 我的&#xff1a; IPv4 地址 . . . . . . . . . . . . : 192.168.92.79 子…

码农必看:常见源代码混淆技术详解

背景 一、项目组代码部署存在的问题 在项目组中&#xff0c;核心代码模块被部署于用户服务器上。然而&#xff0c;另一家公司获取了该服务器的root密码&#xff0c;这就存在着潜在的数据泄露和代码泄露的风险。传统的解决方法是通过配置环境变量来进行数据库加密处理&#xf…

全国项目管理标准化技术委员会副秘书长肖杨先生受邀为第十三届中国PMO大会演讲嘉宾

全国PMO专业人士年度盛会 全国项目管理标准化技术委员会副秘书长、微薄之力&#xff08;北京&#xff09;管理咨询有限公司董事长肖杨先生受邀为PMO评论主办的2024第十三届中国PMO大会演讲嘉宾&#xff0c;演讲议题为“数字化时代下&#xff0c;由职能型组织向高度适应性组织转…

免费的GPT-3.5 API服务aurora

什么是 aurora &#xff1f; aurora 是利用免登录 ChatGPT Web 提供的无限制免费 GPT-3.5-Turbo API 的服务&#xff0c;支持使用 3.5 的 access 调用。 【注意】&#xff1a;仅 IP 属地支持免登录使用 ChatGPT的才可以使用&#xff08;也可以自定义 Baseurl 来绕过限制&#x…

03-JAVA设计模式-装饰模式

装饰模式 什么装饰模式 装饰器模式&#xff08;Decorator Pattern&#xff09;也叫包装器模式&#xff0c;是一种结构型设计模式&#xff0c;允许用户在不改变对象的情况下&#xff0c;动态地给对象增加一些额外的职责&#xff08;功能&#xff09;。装饰器模式相比生成子类更…

【重磅福利】智慧餐饮互联网餐饮行业分析数字化报告大合集共40份(免费下载)

【1】关注本公众号 【2】私信发送 智慧餐饮报告合集 【3】获取本方案合集的下载链接&#xff0c;直接下载即可。

前端学习<四>JavaScript基础——15-内置对象 String:字符串的常见方法

内置对象简介 JavaScript 中的对象分为3种&#xff1a;自定义对象、内置对象、浏览器对象。 前面两种对象&#xff1a;是JS的基础内容&#xff0c;属于 ECMAScript&#xff1b; 第三个浏览器对象&#xff1a;属于JS独有&#xff0c;即 JS 内置的API。 内置对象&#xff1a;就是…

为什么大模型训练都需要GPU?现在都有哪些合适的GPU适合训练大模型?价格如何?

大家有没有这样的疑问&#xff0c;为什么大模型训练需要的是GPU&#xff0c;而不是CPU&#xff0c;而现在市面上&#xff0c;有哪些适合训练的GPU型号&#xff0c;价格如何&#xff1f;下面让我来一一给大家进行介绍。 为什么大模型训练需要GPU&#xff0c;而非CPU&#xff1f;…

设计模式——责任链模式13

责任链模式 每个流程或事物处理 像一个链表结构处理。场景由 多层部门审批&#xff0c;问题分级处理等。下面体现的是 不同难度的问题由不同人进行解决。 设计模式&#xff0c;一定要敲代码理解 传递问题实体 /*** author ggbond* date 2024年04月10日 07:48*/ public class…

Proteus与Multisim哪款更适合51单片机仿真?

选择使用Proteus或Multisim进行51单片机仿真&#xff0c;取决于用户的具体需求、个人偏好以及软件的特点。以下是关于这两款软件的对比分析&#xff1a; 功能和特性 Proteus是一款功能强大的电路设计和仿真软件&#xff0c;它支持多种单片机和微控制器的仿真&#xff0c;包括51…

最新小红书店铺开店带货教程,新手也能跑通全流程(6节课+爆款公式)

爆款标题公式 一、0粉丝个人开店适合哪些赛道,mp4 二、小红书起号爆款逻辑,mp4 三、如何找爆款选题蹭热点.mp4 四、抖音一件代发怎么操作,mp4 五、1688一件代发怎么操作,mp4 六、顾客退货怎么办,mp4 网盘自动获取 链接&#xff1a;https://pan.baidu.com/s/1lpzKPim76qe…