优化
上一个递归下载m3u8视频,需要自己在代码中修改key地址.
这个版本的优点是,如果m3u8文件中存在
URI="https://*********/key.key"
可以通过正则匹配找到key地址,而不用手动修改了.
缺点是,如果m3u8文件中的key地址不完整,或不是以以上方式描述的key地址,那么还是需要重新手动修改key地址的.
1.导入模块
import os
from concurrent.futures import ThreadPoolExecutor
import re
import requests
from Crypto.Cipher import AES
from tqdm import tqdm
2.执行过程
# m3u8文件中包含完整的key地址
def main():
# 输入电影名称
title = input('请输入电影名称:').strip()
# 是否存在video目录
if not os.path.isdir('video'):
os.mkdir('video')
# 获取ts列表和ts列表的索引列表
lst, lst_n = get_lst()
# 下载ts文件
thread_download(lst, lst_n)
# 判断是否需要解码
key = judgment()
# 如果key=None,不需要,否则需要
if key is not None:
key_url = key.group(1)
thread_decode(lst, lst_n, key_url)
# 合并ts文件
merge(len(lst), title)
if __name__ == '__main__':
main()
# m3u8文件中key地址不完整,就用这个
def main():
title = input('请输入电影名称:').strip()
if not os.path.isdir('video'):
os.mkdir('video')
lst, lst_n = get_lst()
thread_download(lst, lst_n)
key = input('是否需要解密(输入Y/N):').strip().upper()
if key == 'Y':
key_url = input('请输入key_url:')
thread_decode(lst, lst_n, key_url)
merge(len(lst), title)
if __name__ == '__main__':
main()
3.获取ts网址列表
def get_lst():
task = []
num = []
with open('1.m3u8', 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if line.startswith('#'):
continue
line = line
task.append(line)
for i in range(len(task)):
num.append(i)
return task, num
4.多线程下载
def download(url, name):
resp = requests.get(url, timeout=10)
with open(f'video/{name}', 'wb') as f1:
f1.write(resp.content)
print(f'{name}下载完成')
def thread_download(lst, lst_n):
with ThreadPoolExecutor(50) as t:
for i in lst_n:
t.submit(download, name=i, url=lst[i])
check(lst, lst_n)
def check(lst, lst_n):
lst_ns = []
for i in lst_n:
if not os.path.isfile(f'video/{i}'):
lst_ns.append(i)
if lst_ns:
print(lst_ns)
print('开始递归==================================================')
thread_download(lst, lst_ns)
else:
print('全部下载完成')
5. 判断是否需要解密
def judgment():
with open('1.m3u8', 'r', encoding='utf-8') as f:
obj = re.compile(r'URI="(.*?)"')
key = obj.search(f.read())
return key
6.如果需要解密
key = judgment()
if key is not None:
key_url = key.group(1)
thread_decode(lst, lst_n, key_url)
def decode_ts(name, url):
resp = requests.get(url)
key = resp.content
iv = b'0000000000000000'
aes = AES.new(key, AES.MODE_CBC, iv)
with open(f'video/{name}', 'rb') as f:
de_f = aes.decrypt(f.read())
with open(f'video/{name}_1', 'wb') as f1:
f1.write(de_f)
print(f'{name}解码完成')
def thread_decode(lst, lst_n, key_url):
with ThreadPoolExecutor(50) as t:
for i in lst_n:
t.submit(decode_ts, name=i, url=key_url)
check_de_ts(lst, lst_n, key_url)
def check_de_ts(lst, lst_n, key_url):
lst_ns = []
for i in lst_n:
if not os.path.isfile(f'video/{i}_1'):
lst_ns.append(i)
if lst_ns:
print(lst_ns)
print('开始递归==================================================')
thread_download(lst, lst_ns)
thread_decode(lst, lst_ns, key_url)
else:
print('全部ts解密完成')
7.合并视频
def merge(num, title):
with open(f'video/{title}.mp4', 'wb') as f:
for i in tqdm(range(num)):
if os.path.isfile(f'video/{i}_1'):
with open(f'video/{i}_1', 'rb') as f1:
f.write(f1.read())
else:
with open(f'video/{i}', 'rb') as f1:
f.write(f1.read())
print('合并完成')
delete_ts(num)
def delete_ts(num):
for i in range(num):
os.remove(f'video/{i}')
if os.path.isfile(f'video/{i}_1'):
os.remove(f'video/{i}_1')
8.完整代码
import os
from concurrent.futures import ThreadPoolExecutor
import re
import requests
from Crypto.Cipher import AES
from tqdm import tqdm
def get_lst():
task = []
num = []
with open('1.m3u8', 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if line.startswith('#'):
continue
line = line
task.append(line)
for i in range(len(task)):
num.append(i)
return task, num
def download(url, name):
resp = requests.get(url, timeout=10)
with open(f'video/{name}', 'wb') as f1:
f1.write(resp.content)
print(f'{name}下载完成')
def thread_download(lst, lst_n):
with ThreadPoolExecutor(50) as t:
for i in lst_n:
t.submit(download, name=i, url=lst[i])
check(lst, lst_n)
def check(lst, lst_n):
lst_ns = []
for i in lst_n:
if not os.path.isfile(f'video/{i}'):
lst_ns.append(i)
if lst_ns:
print(lst_ns)
print('开始递归==================================================')
thread_download(lst, lst_ns)
else:
print('全部下载完成')
def merge(num, title):
with open(f'video/{title}.mp4', 'wb') as f:
for i in tqdm(range(num)):
if os.path.isfile(f'video/{i}_1'):
with open(f'video/{i}_1', 'rb') as f1:
f.write(f1.read())
else:
with open(f'video/{i}', 'rb') as f1:
f.write(f1.read())
print('合并完成')
delete_ts(num)
def decode_ts(name, url):
resp = requests.get(url)
key = resp.content
iv = b'0000000000000000'
aes = AES.new(key, AES.MODE_CBC, iv)
with open(f'video/{name}', 'rb') as f:
de_f = aes.decrypt(f.read())
with open(f'video/{name}_1', 'wb') as f1:
f1.write(de_f)
print(f'{name}解码完成')
def thread_decode(lst, lst_n, key_url):
with ThreadPoolExecutor(50) as t:
for i in lst_n:
t.submit(decode_ts, name=i, url=key_url)
check_de_ts(lst, lst_n, key_url)
def check_de_ts(lst, lst_n, key_url):
lst_ns = []
for i in lst_n:
if not os.path.isfile(f'video/{i}_1'):
lst_ns.append(i)
if lst_ns:
print(lst_ns)
print('开始递归==================================================')
thread_download(lst, lst_ns)
thread_decode(lst, lst_ns, key_url)
else:
print('全部ts解密完成')
def delete_ts(num):
for i in range(num):
os.remove(f'video/{i}')
if os.path.isfile(f'video/{i}_1'):
os.remove(f'video/{i}_1')
def judgment():
with open('1.m3u8', 'r', encoding='utf-8') as f:
obj = re.compile(r'URI="(.*?)"')
key = obj.search(f.read())
return key
def main():
title = input('请输入电影名称:').strip()
if not os.path.isdir('video'):
os.mkdir('video')
lst, lst_n = get_lst()
thread_download(lst, lst_n)
key = judgment()
if key is not None:
key_url = key.group(1)
thread_decode(lst, lst_n, key_url)
merge(len(lst), title)
if __name__ == '__main__':
main()