python脚本下载m3u8视频(支持加密与非加密两种模式)

前言

m3u8视频基本上分为加密与非加密模式 常见的商业视频基本上都是加密模式的(比如小鹅通),也有小厂出的视频是基于非加密模式的,该python脚本下载m3u8视频(支持加密与非加密两种模式)

安装依赖

pip install requests m3u8

代码

import requests
from Crypto.Cipher import AES
import time
import os
import common
import m3u8
import queue
import os
from urllib import parse
import pathlib

header = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
    }

class Downloader():
    def __init__(self, notice_queue:queue.Queue = None) -> None:
        self.notice_queue = notice_queue #通知下载状态
        pass

    def download_m3u8(self, url:str, name:str=None, notice_id = None, save_path:str ='downloads'):
        """下载m3u8视频"""
        #监测目录是否存在
        save_path = pathlib.Path(save_path)
        if not os.path.exists(save_path):
            os.makedirs(save_path)
        m3u8_obj = m3u8.load(url)
        # m3u8_obj.base_path = m3u8_obj.base_uri
        is_encrypted = False #是否加密
        cryptor = None
        if m3u8_obj.keys: #判断是否加密
            is_encrypted = True
            key = m3u8_obj.keys[0]
            keycontent= requests.get(key.uri,headers=header).content
            # print(keycontent)
            #得到解密方法,这里要导入第三方库  pycrypto
            #这里有一个问题,安装pycrypto成功后,导入from Crypto.Cipher import AES报错
            #找到使用python环境的文件夹,在Lib文件夹下有一个 site-packages 文件夹,里面是我们环境安装的包。
            #找到一个crypto文件夹,打开可以看到 Cipher文件夹,此时我们将 crypto文件夹改为 Crypto 即可使用了
            iv = key.iv.replace('0x', '')[:16].encode()  # 去掉前面的标志符,并切片取前16位
            cryptor = AES.new(keycontent, AES.MODE_CBC, iv)

        name = name if name else '{}.mp4'.format(int(round(time.time() * 1000)))
        filename = save_path / name
        total = len(m3u8_obj.segments)
        # m3u8_obj.base_path = m3u8_obj.base_uri
        for index, seg in enumerate(m3u8_obj.segments):
            ts_url = parse.urljoin(m3u8_obj.base_uri, seg.uri)
            print(ts_url)
            res = requests.get(ts_url, header)
            content = res.content
            #使用解密方法解密得到的视频文件
            if is_encrypted:
                content=cryptor.decrypt(content)
            #以追加的形式保存为mp4文件
            with open(filename, 'ab+') as f:
                f.write(content)

            #通知
            target = common.Row()
            target.id = notice_id
            target.p = '{}%'.format(int(index / total * 100))
            if common.g.down_queue:
                common.g.down_queue.put(target)


        pass

if __name__ == '__main__':
    url = ''
    d = Downloader()
    d.download_m3u8(url)
    pass