python下载m3u8文件视频

1.m3u8是什么

m3u8是苹果公司推出的视频播放标准,是基于HTTP的流媒体网络传输协议(HLS)。HLS是把整个流分成一个个小的基于 HTTP 的文件来下载。m3u8 文件实质是一个播放列表(playlist),其内部文字使用的都是 utf-8 编码。
python下载m3u8文件视频_第1张图片
客户端只需按顺序下载上述片段资源,依次进行播放即可。而对于直播来说,客户端需要定时重新请求该 m3u8 文件,看下是否有新的片段数据需要进行下载并播放。

2.下载视频

知道原理开始分流下载片段。其中有些网站的ts文件是采用AES方式进行加密,所以需要对其进行解密。因而我们需要去读取这个key文件,拿取密钥。python下载m3u8文件视频_第2张图片
代码如下:

import m3u8 
import requests
import os
import re
from Crypto.Cipher import AES
import glob
import concurrent.futures
import time
from concurrent.futures import as_completed

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.82 Safari/537.36"
}

#正则表达判断是否为网站地址
def reurl(url):
    pattern = re.compile(r'^((https|http|ftp|rtsp|mms)?:\/\/)[^\s]+')
    m=pattern.search(url)
    if m is None:
        return False
    else:
        return True
 
#获取密钥
def getKey(keystr,url):
    keyinfo= str(keystr)
    method_pos= keyinfo.find('METHOD')
    comma_pos = keyinfo.find(",")
    method = keyinfo[method_pos:comma_pos].split('=')[1]
    uri_pos = keyinfo.find("URI")
    quotation_mark_pos = keyinfo.rfind('"')
    key_url = keyinfo[uri_pos:quotation_mark_pos].split('"')[1]
    if reurl(key_url) == False:
        key_url = url.rsplit("/", 1)[0] + "/" + key_url
    res = requests.get(key_url,headers=headers)
    key = res.content
    print(method)
    print(key.decode('utf-8'))
    return method, key

#下载文件
#down_url:ts文件地址
#url:*.m3u8文件地址
#decrypt:是否加密
#down_path:下载地址
#key:密钥
def download(down_url,url,decrypt,down_path,key):
    if reurl(down_url) == False:
        filename = down_url
        down_url = url.rsplit("/", 1)[0] + "/" + down_url
    else:
        filename = down_url.rsplit("/", 1)[1]
    try:
        res = requests.get(down_url, stream=True, verify=False,headers=headers)
    except Exception as e:
        print(e)
        return
    down_ts_path = down_path+"/{0}".format(filename)
    if decrypt:
        cryptor =  AES.new(key, AES.MODE_CBC, key)
    with open(down_ts_path,"wb+") as file:
        for chunk in res.iter_content(chunk_size=1024):
            if chunk:
                if decrypt:
                    file.write(cryptor.decrypt(chunk))
                else:
                    file.write(chunk)
    
#合并ts文件
#dest_file:合成文件名
#source_path:ts文件目录
#ts_list:文件列表
#delete:合成结束是否删除ts文件   
def merge_to_mp4(dest_file, source_path,ts_list, delete=False):
    files = glob.glob(source_path + '/*.ts')
    if len(files)!=len(ts_list):
        print("文件不完整!")
        return
    with open(dest_file, 'wb') as fw:
        for file in ts_list:
            with open(source_path+"/"+file, 'rb') as fr:
                fw.write(fr.read())
            if delete:
                os.remove(file)

def main():
    url = "https://index.m3u8"
    #使用m3u8库获取文件信息
    video = m3u8.load(url)
    #设置下载路径
    down_path="tmp"
    #设置是否加密标志
    decrypt = False
    #ts列表
    ts_list=[]
    #判断是否加密
    if video.keys[0]  is not None:
        method,key =getKey(video.keys[0],url)
        decrypt = True
     #判断是否需要创建文件夹
    if not os.path.exists(down_path):
        os.mkdir(down_path)
    #把ts文件名添加到列表中
    for filename  in video.segments:
        if reurl(filename.uri):
            ts_list.append(filename.uri.rsplit("/", 1)[1])
        else:
            ts_list.append(filename.uri)
    #开启线程池
    with concurrent.futures.ThreadPoolExecutor() as executor:
        obj_list = []
        begin = time.time()#记录线程开始时间
        for i in range(len(video.segments)):
            obj = executor.submit(download,video.segments[i].uri,url,decrypt,down_path,key)
            obj_list.append(obj)
		#查看线程池是否结束
        for future in as_completed(obj_list):
            data = future.result()
            print(data)
        merge_to_mp4('result.mp4', down_path,ts_list)#合并ts文件
        times = time.time() - begin #记录线程完成时间
        print(times)


if __name__ == "__main__":
    main()

你可能感兴趣的:(Python,m3u8,python,视频)