M3U8视频解密下载

一、按F12,先找出m3u8的url,预览内容是如以下这种类型,然后复制出请求URL

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:16
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-KEY:METHOD=AES-128,URI="https://ke.qq.com/cgi-bin/qcloud/get_dk?edk=CiBENw8S52C77Pg9t2ak1u%2FD9NfhZ3F7%2BoiW%2BM5iBKyMbBCO08TAChiaoOvUBCokOTMyNDg4YmItOWZjYS00MzFiLWJiYjItNjFmMDhjYjNlYmM3&fileId=5285890815554519079&keySource=VodBuildInKMS&token=dWluPTM1MTc4MzU1NDQ7c2tleT1AQklIMFlFYU9SO3Bza2V5PUpPUE44RVUtLXdMUWxqeERZVHUyNEFoa3kqVGh5NGp5clBpbUIzbUJscW9fO3Bsc2tleT0wMDA0MDAwMDc2MTNiOTRmMjA4NmU1NTJjNzhkYzI3NzdhYjQ3NGQxYzZhNGEzN2ZkNTVmMDc4ODZhODFjNDI3MTBmZTA2MTJmN2EwZjFkNzczOTU1OTMyO2V4dD07dWlkX3R5cGU9MDt1aWRfb3JpZ2luX3VpZF90eXBlPTA7Y2lkPTMzODU0OTE7dGVybV9pZD0xMDM1MTg0OTg7dm9kX3R5cGU9MA%3D%3D",IV=0x00000000000000000000000000000000
#EXTINF:2.000000,
v.f30742.ts?start=0&end=506863&type=mpegts&exper=0&sign=4142361e5f70dc1887bb4374a6c8499a&t=60eab641&us=6759179987002315823
#EXT-X-KEY:METHOD=AES-128,URI="https://ke.qq.com/cgi-bin/qcloud/get_dk?edk=CiBENw8S52C77Pg9t2ak1u%2FD9NfhZ3F7%2BoiW%2BM5iBKyMbBCO08TAChiaoOvUBCokOTMyNDg4YmItOWZjYS00MzFiLWJiYjItNjFmMDhjYjNlYmM3&fileId=5285890815554519079&keySource=VodBuildInKMS&token=dWluPTM1MTc4MzU1NDQ7c2tleT1AQklIMFlFYU9SO3Bza2V5PUpPUE44RVUtLXdMUWxqeERZVHUyNEFoa3kqVGh5NGp5clBpbUIzbUJscW9fO3Bsc2tleT0wMDA0MDAwMDc2MTNiOTRmMjA4NmU1NTJjNzhkYzI3NzdhYjQ3NGQxYzZhNGEzN2ZkNTVmMDc4ODZhODFjNDI3MTBmZTA2MTJmN2EwZjFkNzczOTU1OTMyO2V4dD07dWlkX3R5cGU9MDt1aWRfb3JpZ2luX3VpZF90eXBlPTA7Y2lkPTMzODU0OTE7dGVybV9pZD0xMDM1MTg0OTg7dm9kX3R5cGU9MA%3D%3D",IV=0x00000000000000000000000000000000
#EXTINF:2.000000,
v.f30742.ts?start=506864&end=1098319&type=mpegts&exper=0&sign=4142361e5f70dc1887bb4374a6c8499a&t=60eab641&us=6759179987002315823
#EXT-X-KEY:METHOD=AES-128,URI="https://ke.qq.com/cgi-bin/qcloud/get_dk?edk=CiBENw8S52C77Pg9t2ak1u%2FD9NfhZ3F7%2BoiW%2BM5iBKyMbBCO08TAChiaoOvUBCokOTMyNDg4YmItOWZjYS00MzFiLWJiYjItNjFmMDhjYjNlYmM3&fileId=5285890815554519079&keySource=VodBuildInKMS&token=dWluPTM1MTc4MzU1NDQ7c2tleT1AQklIMFlFYU9SO3Bza2V5PUpPUE44RVUtLXdMUWxqeERZVHUyNEFoa3kqVGh5NGp5clBpbUIzbUJscW9fO3Bsc2tleT0wMDA0MDAwMDc2MTNiOTRmMjA4NmU1NTJjNzhkYzI3NzdhYjQ3NGQxYzZhNGEzN2ZkNTVmMDc4ODZhODFjNDI3MTBmZTA2MTJmN2EwZjFkNzczOTU1OTMyO2V4dD07dWlkX3R5cGU9MDt1aWRfb3JpZ2luX3VpZF90eXBlPTA7Y2lkPTMzODU0OTE7dGVybV9pZD0xMDM1MTg0OTg7dm9kX3R5cGU9MA%3D%3D",IV=0x00000000000000000000000000000000
#EXTINF:2.000000,
v.f30742.ts?start=1098320&end=1664207&type=mpegts&exper=0&sign=4142361e5f70dc1887bb4374a6c8499a&t=60eab641&us=6759179987002315823
#EXT-X-KEY:METHOD=AES-128,URI="https://ke.qq.com/cgi-bin/qcloud/get_dk?edk=CiBENw8S52C77Pg9t2ak1u%2FD9NfhZ3F7%2BoiW%2BM5iBKyMbBCO08TAChiaoOvUBCokOTMyNDg4YmItOWZjYS00MzFiLWJiYjItNjFmMDhjYjNlYmM3&fileId=5285890815554519079&keySource=VodBuildInKMS&token=dWluPTM1MTc4MzU1NDQ7c2tleT1AQklIMFlFYU9SO3Bza2V5PUpPUE44RVUtLXdMUWxqeERZVHUyNEFoa3kqVGh5NGp5clBpbUIzbUJscW9fO3Bsc2tleT0wMDA0MDAwMDc2MTNiOTRmMjA4NmU1NTJjNzhkYzI3NzdhYjQ3NGQxYzZhNGEzN2ZkNTVmMDc4ODZhODFjNDI3MTBmZTA2MTJmN2EwZjFkNzczOTU1OTMyO2V4dD07dWlkX3R5cGU9MDt1aWRfb3JpZ2luX3VpZF90eXBlPTA7Y2lkPTMzODU0OTE7dGVybV9pZD0xMDM1MTg0OTg7dm9kX3R5cGU9MA%3D%3D",IV=0x00000000000000000000000000000000
#EXTINF:2.000000,
.....

在这里插入图片描述
在这里插入图片描述

二、拿ts文件请求链接的前缀

(因为第一次拿到的链接,获取到的ts链接是没有前缀的)
随便找一个**v.f30742.ts?**开头的链接,复制它的请求url,只取v.f30742.ts?前面部分
取出链接:https://1258712167.vod2.myqcloud.com/5a81e359vodtranssh1258712167/fe20c2755285890815554519079/drm/
原本链接:https://1258712167.vod2.myqcloud.com/5a81e359vodtranssh1258712167/fe20c2755285890815554519079/drm/v.f30742.ts?start=2184608&end=2711583&type=mpegts&exper=0&sign=4142361e5f70dc1887bb4374a6c8499a&t=60eab641&us=6759179987002315823
在这里插入图片描述

三、对ts文件进行解密下载并合并,代码如下

import re
import requests
import os,uuid
from Crypto.Cipher import AES

class Demo(object):
    '''
        调用方法:
        1、先实例化一个Demo对象
        2、把提取的m3u8url放在url, 把提取的ts  url前缀放在base_url
        2、调用demo的download_ts方法,并将两个参数(url,base_url)传进去
        
        只需要在download方法中传入一个m3u8url即可
    '''
    # 返回当前文件路径
    def get_filpath(self):
        # 先判断ts_files文件夹是否存在,不存在则创建
        if not os.path.exists('ts_files'):
            os.system('mkdir ts_files')
        f_path=os.getcwd()+'\\ts_files\\'
        return f_path

    # 将ts文件合并转换成mp4
    def ts_to_mp4(self):
        path = self.get_filpath()
        mp4_name = str(uuid.uuid1()) + '.mp4'
        print(mp4_name)
        merge_cmd = 'copy /b ' + path + '\*.ts ' + path + '\\' + mp4_name
        #  转换成功后,删除ts文件
        # del_cmd = 'del ' + path + '\*.ts' 

        os.system(merge_cmd)
        # os.system(del_cmd)
        print('转换完成')

    def download_ts(self,url,base_url):
        # 获取初始的ts_urls 和content
        res = requests.get(url=url)
        pay_list = res.text.split('\n')
        ts_urls = pay_list[9:-1:3]

        # 获取加密的方法和密钥
        content=res.text
        method = re.findall('#EXT-X-KEY:METHOD=(.*?),', content)[0]
        # 获取加密url
        key = re.findall('#EXT-X-KEY:METHOD=AES-128,(.*?)\n', content)[0]
        key_url = re.findall('"(.*?)"', key)[0]
        # 对加密url发起请求
        res = requests.get(key_url)
        # 获取返回来的加密密钥
        key = res.content
        # 获取偏移向量 iv
        iv=re.findall('IV=(.*?)\n',content)[0]
		iv=iv.replace('0x','')
        iv=bytes.fromhex(iv)
        cryptor = AES.new(key, AES.MODE_CBC, iv)

        for i, p in enumerate(ts_urls):
            ts_url = base_url + p
            res = requests.get(ts_url)
            '''
            注意:ts文件名,要按001这种形式命令,不然,等下在window下合并顺序会出现错乱,导致视频无法播放
               ts文件比较多的,可以添加位数 如,0001,00001等
                 rjust(3,'0'),3表示位数
            '''
            file_name = str(i).rjust(3,'0') + '.ts'
            file=self.get_filpath()+file_name
            with open(file, 'wb') as f:
                # 对ts文件内容进行解密
                f.write(cryptor.decrypt(res.content))
                print('下载完成')
        self.ts_to_mp4()


if __name__=='__main__':
    # url为第一步提取的url
    url = 'https://1258712167.vod2.myqcloud.com/5a81e359vodtranssh1258712167/fe20c2755285890815554519079/drm/voddrm.token.dWluPTM1MTc4MzU1NDQ7c2tleT1AQklIMFlFYU9SO3Bza2V5PUpPUE44RVUtLXdMUWxqeERZVHUyNEFoa3kqVGh5NGp5clBpbUIzbUJscW9fO3Bsc2tleT0wMDA0MDAwMDc2MTNiOTRmMjA4NmU1NTJjNzhkYzI3NzdhYjQ3NGQxYzZhNGEzN2ZkNTVmMDc4ODZhODFjNDI3MTBmZTA2MTJmN2EwZjFkNzczOTU1OTMyO2V4dD07dWlkX3R5cGU9MDt1aWRfb3JpZ2luX3VpZF90eXBlPTA7Y2lkPTMzODU0OTE7dGVybV9pZD0xMDM1MTg0OTg7dm9kX3R5cGU9MA==.v.f30742.m3u8?exper=0&sign=4142361e5f70dc1887bb4374a6c8499a&t=60eab641&us=6759179987002315823'
    #base_url 为第二步提取的url前缀
    base_url='https://1258712167.vod2.myqcloud.com/5a81e359vodtranssh1258712167/fe20c2755285890815554519079/drm/'
    h = Demo()
    h.download_ts(url=url,base_url=base_url)

注意:最后合成有可能出现,找不到文件的错误提示,原因:你路径有问题,可以自己在cmd进行合并
合并命令如下:

# path是路径
'copy /b '+path+'\*.ts '+ path+'\\new1.mp4'

(2) 如果遇到iv提示报错 ValueError: Incorrect IV length (it must be 16 bytes long)
则需要对iv进行处理

# 如IV=0x0E16B71765DC9B699C33DC5901307461
# 1、先将iv值首两位给剔除掉,因为首两位是0x是16进制标识,去掉后就剩下32位字符串了
iv=IV[2:]
# 2、将32位字符串转成16进制
iv=bytes.fromhex(iv)

最后效果如下:
在这里插入图片描述

下一次,再把多线程加进入,还有linux下的合并方法。
注意:最好用linux合并,window合并上传到服务器可能会播放不了
linux下合并脚本如下:

import os
#ts文件绝对路径
ts_path = '/home/ts_files'
 #读取ts文件夹下所有的ts文件
path_list = os.listdir(ts_path)
 #对文件进行排序
path_list.sort()
 #将排序后的ts的绝对路径放入列表中
li = [os.path.join(ts_path,filename) for filename in path_list]
 #类似于[001.ts|00.2ts|003.ts]
input_file = '|'.join(li)
 #指定输出文件名称
output_file = ts_path + '.mp4'
 #使用ffmpeg将ts合并为mp4
command = 'ffmpeg -i "concat:%s" -acodec copy -vcodec copy -absf aac_adtstoasc %s'%	(input_file,output_file)
 #指行命令
os.system(command)

注意

有的key的url需要加一个token才可以获取到密钥,如:技成培训网站 在这里插入图片描述

使用进程池和线程池、协程池三种方式,可以提高效率,但是,效率有一个峰值。代码如下:

# -*-coding:utf-8-*-
import re
import time

import requests
import os, uuid
from Crypto.Cipher import AES

from gevent import monkey
import gevent
from gevent.pool import Pool
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor

import sys
sys.setrecursionlimit(100000000)
# 获取key 和iv
def get_key_iv(content):
    # 正则获取加密方法,正常都是AES-128
    method = re.findall('#EXT-X-KEY:METHOD=(.*?),', content)[0]
    # 正则获取密钥在服务器的url
    key_url = re.findall('#EXT-X-KEY:METHOD=AES-128,URI="(.*?)"', content)[0]
    # 对key_url发起请求获取密钥,如果请求失败,则在调试面板看是否缺少token等参数
    key = requests.get(key_url).content

    # 获取iv(偏移量)
    iv = re.findall('IV=(.*?)\n', content)[0]
    # 将iv的首两位标识(16进制标识)去除,可以采用切片或者替换
    iv = iv.replace('0x', '')
    # 将iv字符串转成16进制
    iv = bytes.fromhex(iv)
    dic = {
        'key': key,
        'iv': iv
    }
    return dic

# 获取ts列表url
def get_ts_list(content):
    data_list = content.split('\n')
    is_first = 0
    first_num = 0
    for i, data in enumerate(data_list):
        if '.ts' in data and is_first == 0:
            first_num = i
            is_first = 1
        elif '.ts' in data and is_first == 1:
            is_first = i - first_num
            break
    ts_list = data_list[first_num::is_first]
    return ts_list

# 设置保存路径
def set_file():
    file_path = os.getcwd() + '\\tsfiles\\'
    if not os.path.exists(file_path):
        os.makedirs(file_path)
    return file_path

# 合并ts视频
def ts_to_mp4():
    path = set_file()
    mp4_name = str(uuid.uuid1()) + '.mp4'
    merge_cmd = 'copy /b ' + path + '\*.ts ' + path + '\\' + mp4_name
    # 根据需求,看合并后,是否保留ts视频
    del_cmd = 'del ' + path + '\*.ts'
    os.system(merge_cmd)
    # os.system(del_cmd)

def download_ts(dic):
    # 统一默认都是有加密的,没有加密只需把true改为false
    is_cry = True
    i = dic['i']
    ts = dic['ts']
    content = dic['content']
    if is_cry:
        # 获取加密密钥和偏移值
        key = get_key_iv(content).get('key')
        iv = get_key_iv(content).get('iv')
        # 实例化CBC模式的AES对象
        cryptor = AES.new(key, AES.MODE_CBC, iv)

    res = requests.get(ts)
    # 可以根据ts文件数量,适当设置3(3表示是位数,如 001)
    file_name = str(i).rjust(3, '0') + '.ts'
    file = set_file() + file_name
    with open(file, 'wb') as f:
        if is_cry:
            content = cryptor.decrypt(res.content)
        else:
            content = res.content
        f.write(content)
        print('ts{}下载成功'.format(i))

if __name__ == '__main__':
    url = 'https://1258712167.vod2.myqcloud.com/25121a6avodtransbj1258712167/3522b9c75285890816911210574/drm/voddrm.token.dWluPTM1MTc4MzU1NDQ7c2tleT1AUUdUU0JlNzlBO3Bza2V5PVVkUXIwQWVZbVZjUWV4RVFzQk9TWUk5cypPSXZDKk51dXF2S25VSU9NdGdfO3Bsc2tleT0wMDA0MDAwMDNhYzI1YjE5ODFhNzVlZWFkMjNkOGNmMGQ5ODA5Mjk3ZTJlY2E4MzRhMTU5ZDgzM2FjYjU3MzYwYWZjNTk0YTQwZmNjZjBhYzJmNWU3ZWYwO2V4dD07dWlkX3R5cGU9MDt1aWRfb3JpZ2luX3VpZF90eXBlPTA7Y2lkPTM5OTAxNzt0ZXJtX2lkPTEwMDQ3NTk2NTt2b2RfdHlwZT0w.v.f30741.m3u8?exper=0&sign=81967dbbdb625de8455b566adf35c6d2&t=61206ef8&us=269233269408429750'
    base_url = 'https://1258712167.vod2.myqcloud.com/25121a6avodtransbj1258712167/3522b9c75285890816911210574/drm/'
    content = requests.get(url).text
    ts_list = get_ts_list(content)
    pool = Pool(10)  #协程的
    # 进程池线程池
    # pool= ThreadPoolExecutor(50)
    # pool = ProcessPoolExecutor(20)
    s = time.time()
    g_list=[]
    print(len(ts_list))
    for i, ts in enumerate(ts_list):
        ts =base_url+ts
        # download_ts({'ts': ts, 'i': i, 'content': content})
        # 协程
        g=pool.spawn(download_ts,({'ts':ts,'i':i,'content':content}))
        # 线程、进程
        # pool.submit(download_ts,({'ts':ts,'i':i,'content':content}))

        # 协程
        g_list.append(g)
    # 进程线程
    # pool.shutdown(wait=True)
    # 协程
    gevent.joinall(g_list)
    print(time.time()-s)

'''
正常情况下是:342.5760555267334
协程池:70多秒
进程池和线程池: 50-70多秒左右
'''
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值